﻿// <copyright file="ColumnAttribute.cs" company="PetaPoco - CollaboratingPlatypus">
//      Apache License, Version 2.0 https://github.com/CollaboratingPlatypus/PetaPoco/blob/master/LICENSE.txt
// </copyright>
// <author>PetaPoco - CollaboratingPlatypus</author>
// <date>2017/05/17</date>
// <Version> 5.1.259 </Version>

// --------------------------WARNING--------------------------------
// -----------------------------------------------------------------
// This file was built by merging separate C# source files into one.        该文件是通过将单独的C＃源文件合并为一个构建的。
// DO NOT EDIT THIS FILE. Any edits will be overwritten when the       不要编辑这个文件。 任何编辑将被覆盖
// nuget package is updated or reinstalled. If changes are required       nuget包被更新或重新安装。 如果需要更改
// extend the Database class and place in a separate file.                      扩展Database类并放在一个单独的文件中。				
// -----------------------------------------------------------------
// --------------------------WARNING--------------------------------


using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data.Common;
using System.Data;
using System.Dynamic;
using System.Linq.Expressions;
using System.Linq;
using System.Reflection.Emit;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Text;
using System.Threading;
using System;

namespace WenCong.WorkShop.FrameWork.Orm
{
#pragma warning disable 1066,1570,1573,1591

    /// <summary>
    ///     The main PetaPoco Database class.  You can either use this class directly, or derive from it.主要的PetaPoco数据库类。 你可以直接使用这个类，也可以从它派生。
    /// </summary>
    public class Database : IDatabase
    {
        #region IDisposable+自动关闭一个打开的共享连接

        /// <summary>
        ///     Automatically close one open shared connection自动关闭一个打开的共享连接
        /// </summary>
        public void Dispose()
        {
            // Automatically close one open connection reference自动关闭一个打开的连接引用
            //  (Works with KeepConnectionAlive and manually opening a shared connection)（使用KeepConnectionAlive并手动打开共享连接）
            CloseSharedConnection();
        }

        #endregion

        #region Constructors

        /// <summary>
        ///     Constructs an instance using the first connection string found in the app/web configuration file.使用app / web配置文件中找到的第一个连接字符串构造一个实例。
        /// </summary>
        /// <exception cref="InvalidOperationException">Thrown when no connection strings can registered.当没有连接字符串可以注册时抛出。</exception>
        public Database()
        {
            if (ConfigurationManager.ConnectionStrings.Count == 0)
                throw new InvalidOperationException("One or more connection strings must be registered to use the no paramater constructor");//必须注册一个或多个连接字符串才能使用无参数构造函数

            var entry = ConfigurationManager.ConnectionStrings[0];
            _connectionString = entry.ConnectionString;
            var providerName = !string.IsNullOrEmpty(entry.ProviderName) ? entry.ProviderName : "System.Data.SqlClient";
            Initialise(DatabaseProvider.Resolve(providerName, false, _connectionString), null);
        }

        /// <summary>
        ///     Constructs an instance using a supplied IDbConnection.使用提供的IDbConnection构造一个实例。
        /// </summary>
        /// <param name="connection">The IDbConnection to use.要使用的IDbConnection。</param>
        /// <remarks>
        ///     The supplied IDbConnection will not be closed/disposed by PetaPoco - that remains the responsibility of the caller.
        ///     提供的IDbConnection将不会被PetaPoco关闭/处理 - 这仍然是调用者的责任。
        /// </remarks>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connection" /> is null or empty.</exception>当connection为null or empty时抛出。
        public Database(IDbConnection connection)
        {
            if (connection == null)
                throw new ArgumentNullException("connection");

            _sharedConnection = connection;
            _connectionString = connection.ConnectionString;
            // Prevent closing external connection防止关闭外部连接
            _sharedConnectionDepth = 2;

            Initialise(DatabaseProvider.Resolve(_sharedConnection.GetType(), false, _connectionString), null);
        }

        /// <summary>
        ///     Constructs an instance using a supplied connections string and optionally a provider name. If no provider name is given, the default database provider will be MS SQL Server.
        ///     使用提供的连接字符串和可选的提供程序名称构造一个实例。 如果没有提供提供者名称，则默认数据库提供者将是MS SQL Server。
        /// </summary>
        /// <param name="connectionString">The database connection string.数据库连接字符串。</param>
        /// <param name="providerName">The database provider name, if given.数据库提供程序名称，如果给定。</param>
        /// <remarks>
        ///     PetaPoco will automatically close and dispose any connections it creates.PetaPoco将自动关闭并处理其创建的任何连接。
        /// </remarks>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connectionString" /> is null or empty.</exception>当connectionString为null or empty时抛出。
        public Database(string connectionString, string providerName = null)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentException("Connection string cannot be null or empty", "connectionString");

            _connectionString = connectionString;
            Initialise(DatabaseProvider.Resolve(providerName, true, _connectionString), null);
        }

        /// <summary>
        ///     Constructs an instance using the supplied connection string and DbProviderFactory.--使用提供的连接字符串和DbProviderFactory构造一个实例。
        /// </summary>
        /// <param name="connectionString">The database connection string.--数据库连接字符串。</param>
        /// <param name="factory">The DbProviderFactory to use for instantiating IDbConnection's.--用于实例化IDbConnection的DbProviderFactory。</param>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connectionString" /> is null or empty.</exception>--当connectionString为null or empty时抛出。
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="factory" /> is null.</exception>--当factory为null 时抛出。
        public Database(string connectionString, DbProviderFactory factory)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentException("Connection string must not be null or empty", "connectionString");

            if (factory == null)
                throw new ArgumentNullException("factory");

            _connectionString = connectionString;
            Initialise(DatabaseProvider.Resolve(DatabaseProvider.Unwrap(factory).GetType(), false, _connectionString), null);
        }

        /// <summary>
        ///     Constructs an instance using a supplied connection string name. The actual connection string and provider will be read from app/web.config.
        ///     使用提供的连接字符串名构造一个实例。 实际的连接字符串和提供者将从app / web.config中读取。
        /// </summary>
        /// <param name="connectionStringName">The name of the connection.连接的名称。</param>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connectionStringName" /> is null or empty.</exception>当connectionStringName为null 时抛出。
        /// <exception cref="InvalidOperationException">Thrown when a connection string cannot be found.当无法找到连接字符串时抛出。</exception>
        public Database(string connectionStringName)
        {
            if (string.IsNullOrEmpty(connectionStringName))
                throw new ArgumentException("Connection string name must not be null or empty", "connectionStringName");

            var entry = ConfigurationManager.ConnectionStrings[connectionStringName];

            if (entry == null)
                throw new InvalidOperationException(string.Format("Can't find a connection string with the name '{0}'", connectionStringName));

            _connectionString = entry.ConnectionString;
            var providerName = !string.IsNullOrEmpty(entry.ProviderName) ? entry.ProviderName : "System.Data.SqlClient";
            Initialise(DatabaseProvider.Resolve(providerName, false, _connectionString), null);
        }

        /// <summary>
        ///     Constructs an instance using the supplied provider and optional default mapper.使用提供的提供程序和可选的默认映射器构造一个实例。
        /// </summary>
        /// <param name="connectionString">The database connection string.数据库连接字符串。</param>
        /// <param name="provider">The provider to use.要使用的provider</param>
        /// <param name="defaultMapper">The default mapper to use when no specific mapper has been registered.当没有注册特定映射器时使用的默认映射器。</param>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connectionString" /> is null or empty.</exception>当connectionString为null or empty 时抛出。
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="provider" /> is null.--当<paramref name =“provider”/>为空时抛出。</exception>当provider为null  时抛出
        public Database(string connectionString, IProvider provider, IMapper defaultMapper = null)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentException("Connection string must not be null or empty", "connectionString");

            if (provider == null)
                throw new ArgumentNullException("provider");

            _connectionString = connectionString;
            Initialise(provider, defaultMapper);
        }

        /// <summary>
        ///     Constructs an instance using the supplied <paramref name="configuration"/>.使用提供的configuration构造一个实例
        /// </summary>
        /// <param name="configuration">The configuration for constructing an instance.构建实例的配置。</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="configuration" /> is null.</exception>当configuration为null  时抛出
        /// <exception cref="InvalidOperationException">Thrown when no configuration string is configured and app/web config does any connection string registered.当没有配置字符串被配置并且app / web配置执行任何连接字符串注册时抛出。</exception>
        /// <exception cref="InvalidOperationException">Thrown when a connection string configured and no provider is configured.当配置连接字符串并且没有配置提供程序时抛出。</exception>
        public Database(IDatabaseBuildConfiguration configuration)
        {
            if (configuration == null)
                throw new ArgumentNullException("configuration");

            var settings = (IBuildConfigurationSettings)configuration;

            IMapper defaultMapper = null;
            settings.TryGetSetting<IMapper>(DatabaseConfigurationExtensions.DefaultMapper, v => defaultMapper = v);

            ConnectionStringSettings entry = null;
            settings.TryGetSetting<string>(DatabaseConfigurationExtensions.ConnectionString, cs => _connectionString = cs, () =>
            {
                settings.TryGetSetting<string>(DatabaseConfigurationExtensions.ConnectionStringName, cn =>
                {
                    entry = ConfigurationManager.ConnectionStrings[cn];

                    if (entry == null)
                        throw new InvalidOperationException(string.Format("Can't find a connection string with the name '{0}'", cn));
                }, () =>
                {
                    if (ConfigurationManager.ConnectionStrings.Count == 0)
                        throw new InvalidOperationException("One or more connection strings must be registered to not configure the connection string");

                    entry = ConfigurationManager.ConnectionStrings[0];
                });

                // ReSharper disable once PossibleNullReferenceException //ReSharper禁用一次可能的NullReferenceException
                _connectionString = entry.ConnectionString;
            });

            settings.TryGetSetting<IProvider>(DatabaseConfigurationExtensions.Provider, v => Initialise(v, defaultMapper), () =>
            {
                if (entry == null)
                    throw new InvalidOperationException("Both a connection string and provider are required or neither.");

                var providerName = !string.IsNullOrEmpty(entry.ProviderName) ? entry.ProviderName : "System.Data.SqlClient";
                Initialise(DatabaseProvider.Resolve(providerName, false, _connectionString), defaultMapper);
            });

            settings.TryGetSetting<bool>(DatabaseConfigurationExtensions.EnableNamedParams, v => EnableNamedParams = v);
            settings.TryGetSetting<bool>(DatabaseConfigurationExtensions.EnableAutoSelect, v => EnableAutoSelect = v);
            settings.TryGetSetting<int>(DatabaseConfigurationExtensions.CommandTimeout, v => CommandTimeout = v);
            settings.TryGetSetting<IsolationLevel>(DatabaseConfigurationExtensions.IsolationLevel, v => IsolationLevel = v);
        }

        /// <summary>
        ///     Provides common initialization for the various constructors.为各种构造函数提供常用的初始化。
        /// </summary>
        private void Initialise(IProvider provider, IMapper mapper)
        {
            // Reset 重启
            _transactionDepth = 0;
            EnableAutoSelect = true;
            EnableNamedParams = true;

            // What character is used for delimiting parameters in SQL //什么字符用于在SQL中定义参数
            _provider = provider;
            _paramPrefix = _provider.GetParameterPrefix(_connectionString);
            _factory = _provider.GetFactory();

            _defaultMapper = mapper ?? new ConventionMapper();
        }

        #endregion

        #region Connection Management

        /// <summary>
        ///     When set to true the first opened connection is kept alive until this object is disposed//当设置为true时，第一个打开的连接将保持活动，直到此对象被处理为止
        /// </summary>
        public bool KeepConnectionAlive { get; set; }

        /// <summary>
        ///     Open a connection that will be used for all subsequent queries.//打开将用于所有后续查询的连接。
        /// </summary>
        /// <remarks>
        ///     Calls to Open/CloseSharedConnection are reference counted and should be balanced//对OpenSharedConnection / CloseSharedConnection的调用是引用计数，应平衡
        /// </remarks>
        public void OpenSharedConnection()
        {
            if (_sharedConnectionDepth == 0)
            {
                _sharedConnection = _factory.CreateConnection();
                _sharedConnection.ConnectionString = _connectionString;

                if (_sharedConnection.State == ConnectionState.Broken)
                    _sharedConnection.Close();

                if (_sharedConnection.State == ConnectionState.Closed)
                    _sharedConnection.Open();

                _sharedConnection = OnConnectionOpened(_sharedConnection);

                if (KeepConnectionAlive)
                    _sharedConnectionDepth++; // Make sure you call Dispose//确保你调用Dispose
            }
            _sharedConnectionDepth++;
        }

        /// <summary>
        ///     Releases the shared connection释放共享连接
        /// </summary>
        public void CloseSharedConnection()
        {
            if (_sharedConnectionDepth > 0)
            {
                _sharedConnectionDepth--;
                if (_sharedConnectionDepth == 0)
                {
                    OnConnectionClosing(_sharedConnection);
                    _sharedConnection.Dispose();
                    _sharedConnection = null;
                }
            }
        }

        /// <summary>
        ///     Provides access to the currently open shared connection (or null if none)//提供对当前打开的共享连接的访问（如果没有）
        /// </summary>
        public IDbConnection Connection
        {
            get { return _sharedConnection; }
        }

        #endregion

        #region Transaction Management事务管理

        /// <summary>
        ///     Gets the current transaction instance.获取当前事务实例。
        /// </summary>
        /// <returns>
        ///     The current transaction instance; else, <c>null</c> if not transaction is in progress.当前的事务实例; 否则，如果不是正在进行事务，
        /// </returns>
        IDbTransaction ITransactionAccessor.Transaction
        {
            get { return _transaction; }
        }

        // Helper to create a transaction scope//创建一个事务的帮助方法

        /// <summary>
        ///     Starts or continues a transaction.开始或继续事务。
        /// </summary>
        /// <returns>An ITransaction reference that must be Completed or disposed必须完成或处置的ITransaction参考</returns>
        /// <remarks>
        ///     This method makes management of calls to Begin/End/CompleteTransaction easier.这种方法使得对Begin / End / CompleteTransaction的调用更容易管理。
        ///     The usage pattern for this should be://这个使用模式应该是：
        ///     using (var tx = db.GetTransaction())
        ///     {
        ///     // Do stuff
        ///     db.Update(...);
        ///     // Mark the transaction as complete
        ///     tx.Complete();
        ///     }
        ///     Transactions can be nested but they must all be completed otherwise the entire事务可以嵌套，但是它们必须全部被完成
        ///     transaction is aborted.事务中止。
        /// </remarks>
        public ITransaction GetTransaction()
        {
            return new Transaction(this);
        }

        /// <summary>
        ///     Called when a transaction starts.  Overridden by the T4 template generated database classes to ensure the same DB instance is used throughout the transaction.
        ///     在事务开始时调用。 由T4模板生成的数据库类覆盖，以确保在整个事务中使用相同的DB实例。
        /// </summary>
        public virtual void OnBeginTransaction()
        {
        }

        /// <summary>
        ///     Called when a transaction ends.在事务结束时调用。
        /// </summary>
        public virtual void OnEndTransaction()
        {
        }

        /// <summary>
        ///     Starts a transaction scope, see GetTransaction() for recommended usage启动事务，请参阅GetTransaction（）以获取推荐用法
        /// </summary>
        public void BeginTransaction()
        {
            _transactionDepth++;

            if (_transactionDepth == 1)
            {
                OpenSharedConnection();
                _transaction = !_isolationLevel.HasValue ? _sharedConnection.BeginTransaction() : _sharedConnection.BeginTransaction(_isolationLevel.Value);
                _transactionCancelled = false;
                OnBeginTransaction();
            }
        }

        /// <summary>
        ///     Internal helper to cleanup transaction内部帮助清理事务方法
        /// </summary>
        private void CleanupTransaction()
        {
            OnEndTransaction();

            if (_transactionCancelled)
                _transaction.Rollback();
            else
                _transaction.Commit();

            _transaction.Dispose();
            _transaction = null;

            CloseSharedConnection();
        }

        /// <summary>
        ///     Aborts the entire outer most transaction scope中止整个外部的最大事务范围
        /// </summary>
        /// <remarks>
        ///     Called automatically by Transaction.Dispose()  if the transaction wasn't completed.
        ///    如果事务未完成，则由Transaction.Dispose（）自动调用。
        /// </remarks>
        public void AbortTransaction()
        {
            _transactionCancelled = true;
            if ((--_transactionDepth) == 0)
                CleanupTransaction();
        }

        /// <summary>
        ///     Marks the current transaction scope as complete.将当前事务范围标记为完整。
        /// </summary>
        public void CompleteTransaction()
        {
            if ((--_transactionDepth) == 0)
                CleanupTransaction();
        }

        #endregion

        #region Command Management命令管理

        /// <summary>
        ///     Add a parameter to a DB command将参数添加到DB命令
        /// </summary>
        /// <param name="cmd">A reference to the IDbCommand to which the parameter is to be added对要添加参数的IDbCommand的引用</param>
        /// <param name="value">The value to assign to the parameter分配给参数的值</param>
        /// <param name="pi">Optional, a reference to the property info of the POCO property from which the value is coming.可选的是引用值来自的POCO属性的属性信息。</param>
        private void AddParam(IDbCommand cmd, object value, PropertyInfo pi)
        {
            // Convert value to from poco type to db type将值从poco类型转换为数据库类型
            if (pi != null)
            {
                var mapper = Mappers.GetMapper(pi.DeclaringType, _defaultMapper);
                var fn = mapper.GetToDbConverter(pi);
                if (fn != null)
                    value = fn(value);
            }

            // Support passed in parameters支持通过参数传递
            var idbParam = value as IDbDataParameter;
            if (idbParam != null)
            {
                idbParam.ParameterName = string.Format("{0}{1}", _paramPrefix, cmd.Parameters.Count);
                cmd.Parameters.Add(idbParam);
                return;
            }

            // Create the parameter创建参数
            var p = cmd.CreateParameter();
            p.ParameterName = string.Format("{0}{1}", _paramPrefix, cmd.Parameters.Count);

            // Assign the parmeter value分配参数值
            if (value == null)
            {
                p.Value = DBNull.Value;

                if (pi != null && pi.PropertyType.Name == "Byte[]")
                {
                    p.DbType = DbType.Binary;
                }
            }
            else
            {
                // Give the database type first crack at converting to DB required type给数据库类型首先破解转换为DB所需类型
                value = _provider.MapParameterValue(value);

                var t = value.GetType();
                if (t.IsEnum) // PostgreSQL .NET driver wont cast enum to int//PostgreSQL .NET驱动程序不会将枚举转换为int
                {
                    p.Value = Convert.ChangeType(value, ((Enum)value).GetTypeCode());
                }
                else if (t == typeof(Guid) && !_provider.HasNativeGuidSupport)
                {
                    p.Value = value.ToString();
                    p.DbType = DbType.String;
                    p.Size = 40;
                }
                else if (t == typeof(string))
                {
                    // out of memory exception occurs if trying to save more than 4000 characters to SQL Server CE NText column. Set before attempting to set Size, or Size will always max out at 4000
                    //如果尝试将超过4000个字符保存到SQL Server CE NText列，则出现内存异常。 尝试设置Size之前设置，否则Size将始终最大为4000
                    if ((value as string).Length + 1 > 4000 && p.GetType().Name == "SqlCeParameter")
                        p.GetType().GetProperty("SqlDbType").SetValue(p, SqlDbType.NText, null);

                    p.Size = Math.Max((value as string).Length + 1, 4000); // Help query plan caching by using common size//通过使用常规大小帮助查询计划缓存
                    p.Value = value;
                }
                else if (t == typeof(AnsiString))
                {
                    // Thanks @DataChomp for pointing out the SQL Server indexing performance hit of using wrong string type on varchar
                    //感谢@DataChomp指出在varchar上使用错误字符串类型的SQL Server索引性能命中
                    p.Size = Math.Max((value as AnsiString).Value.Length + 1, 4000);
                    p.Value = (value as AnsiString).Value;
                    p.DbType = DbType.AnsiString;
                }
                else if (value.GetType().Name == "SqlGeography") //SqlGeography is a CLR Type//SqlGeography是一个CLR类型
                {
                    p.GetType().GetProperty("UdtTypeName").SetValue(p, "geography", null); //geography is the equivalent SQL Server Type//geography是等效的SQL Server类型
                    p.Value = value;
                }
                else if (value.GetType().Name == "SqlGeometry") //SqlGeometry is a CLR Type//SqlGeometry是一个CLR类型
                {
                    p.GetType().GetProperty("UdtTypeName").SetValue(p, "geometry", null); //geometry is the equivalent SQL Server Type//geometry是等效的SQL Server类型
                    p.Value = value;
                }
                else
                {
                    p.Value = value;
                }
            }

            // Add to the collection
            cmd.Parameters.Add(p);
        }

        // Create a command
        private static Regex rxParamsPrefix = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);

        public IDbCommand CreateCommand(IDbConnection connection, string sql, params object[] args)
        {
            // Perform named argument replacements执行命名参数替换
            if (EnableNamedParams)
            {
                var new_args = new List<object>();
                sql = ParametersHelper.ProcessParams(sql, args, new_args);
                args = new_args.ToArray();
            }

            // Perform parameter prefix replacements执行参数前缀替换
            if (_paramPrefix != "@")
                sql = rxParamsPrefix.Replace(sql, m => _paramPrefix + m.Value.Substring(1));
            sql = sql.Replace("@@", "@"); // <- double @@ escapes a single @

            // Create the command and add parameters创建命令并添加参数
            IDbCommand cmd = connection.CreateCommand();
            cmd.Connection = connection;
            cmd.CommandText = sql;
            cmd.Transaction = _transaction;
            foreach (var item in args)
            {
                AddParam(cmd, item, null);
            }

            // Notify the DB type通知数据库类型
            _provider.PreExecute(cmd);

            // Call logging 通话记录
            if (!String.IsNullOrEmpty(sql))
                DoPreExecute(cmd);

            return cmd;
        }

        #endregion

        #region Exception Reporting and Logging异常报告和日志记录

        /// <summary>
        ///     Called if an exception occurs during processing of a DB operation.  Override to provide custom logging/handling.在处理DB操作期间发生异常时调用。 覆盖以提供自定义日志记录/处理。
        /// </summary>
        /// <param name="x">The exception instance异常实例</param>
        /// <returns>True to re-throw the exception, false to suppress it 返回true重新抛出异常，返回false来抑制它</returns>
        public virtual bool OnException(Exception x)
        {
            System.Diagnostics.Debug.WriteLine(x.ToString());
            System.Diagnostics.Debug.WriteLine(LastCommand);
            return true;
        }

        /// <summary>
        ///     Called when DB connection opened当DB连接打开时调用
        /// </summary>
        /// <param name="conn">The newly opened IDbConnection新打开的IDbConnection</param>
        /// <returns>The same or a replacement IDbConnection相同或替换的IDbConnection</returns>
        /// <remarks>
        ///     Override this method to provide custom logging of opening connection, or to provide a proxy IDbConnection.
        ///     覆盖此方法以提供打开连接的自定义日志记录，或提供代理IDbConnection。
        /// </remarks>
        public virtual IDbConnection OnConnectionOpened(IDbConnection conn)
        {
            return conn;
        }

        /// <summary>
        ///     Called when DB connection closed当DB连接关闭时调用
        /// </summary>
        /// <param name="conn">The soon to be closed IDBConnection//即将关闭IDBConnection</param>
        public virtual void OnConnectionClosing(IDbConnection conn)
        {
        }

        /// <summary>
        ///     Called just before an DB command is executed--在执行DB命令之前调用
        /// </summary>
        /// <param name="cmd">The command to be executed--要执行的命令</param>
        /// <remarks>
        ///     Override this method to provide custom logging of commands and/or modification of the IDbCommand before it's executed
        ///     覆盖此方法以在执行命令之前提供自定义日志记录和/或修改IDbCommand
        /// </remarks>
        public virtual void OnExecutingCommand(IDbCommand cmd)
        {
        }

        /// <summary>
        ///     Called on completion of command execution--在完成命令执行时调用
        /// </summary>
        /// <param name="cmd">The IDbCommand that finished executing--完成执行的IDbCommand</param>
        public virtual void OnExecutedCommand(IDbCommand cmd)
        {
        }

        #endregion

        #region operation: Execute 执行非查询命令（增删改操作）

        /// <summary>
        ///     Executes a non-query command执行非查询命令（增删改操作）
        /// </summary>
        /// <param name="sql">The SQL statement to execute要执行的SQL语句</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        public int Execute(string sql, params object[] args)
        {
            try
            {
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        var retv = cmd.ExecuteNonQuery();
                        OnExecutedCommand(cmd);
                        return retv;
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
            catch (Exception x)
            {
                if (OnException(x))
                    throw;
                return -1;
            }
        }

        /// <summary>
        ///    Executes a non-query command执行非查询命令（增删改操作）
        /// </summary>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        public int Execute(Sql sql)
        {
            return Execute(sql.SQL, sql.Arguments);
        }

        #endregion

        #region operation: ExecuteScalar

        /// <summary>
        ///     Executes a query and return the first column of the first row in the result set.//执行查询并返回结果集中第一行的第一列。
        /// </summary>
        /// <typeparam name="T">The type that the result value should be cast to//要转换结果值的类型</typeparam>
        /// <param name="sql">The SQL query to execute//要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The scalar value cast to T //返回泛型T类型</returns>
        public T ExecuteScalar<T>(string sql, params object[] args)
        {
            try
            {
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, sql, args))
                    {
                        object val = cmd.ExecuteScalar();
                        OnExecutedCommand(cmd);

                        // Handle nullable types//处理可空类型
                        Type u = Nullable.GetUnderlyingType(typeof(T));
                        if (u != null && (val == null || val == DBNull.Value))
                            return default(T);

                        return (T)Convert.ChangeType(val, u == null ? typeof(T) : u);
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
            catch (Exception x)
            {
                if (OnException(x))
                    throw;
                return default(T);
            }
        }

        /// <summary>
        ///      Executes a query and return the first column of the first row in the result set.//执行查询并返回结果集中第一行的第一列。
        /// </summary>
        /// <typeparam name="T">The type that the result value should be cast to//要转换结果值的类型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The scalar value cast to T //返回泛型T类型</returns>
        public T ExecuteScalar<T>(Sql sql)
        {
            return ExecuteScalar<T>(sql.SQL, sql.Arguments);
        }

        #endregion

        #region operation: Fetch

        /// <summary>
        ///     Runs a query and returns the result set as a typed list运行查询并将结果集返回为类型列表
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query to execute//要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>>A List holding the results of the query//包含查询结果的列表</returns>
        public List<T> Fetch<T>(string sql, params object[] args)
        {
            return Query<T>(sql, args).ToList();
        }

        /// <summary>
        ///     Runs a query and returns the result set as a typed list运行查询并将结果集返回为类型列表
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>>A List holding the results of the query//包含查询结果的列表</returns>
        public List<T> Fetch<T>(Sql sql)
        {
            return Fetch<T>(sql.SQL, sql.Arguments);
        }

        #endregion

        #region operation: Page

        /// <summary>
        ///     Starting with a regular SELECT statement, derives the SQL statements required to query a DB for a page of records and the total number of records
        ///     从一个常规的SELECT语句开始，导出查询DB页面记录所需的SQL语句和总记录数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="skip">The number of rows to skip before the start of the page//在页面开始之前要跳过的行数</param>
        /// <param name="take">The number of rows in the page//页面中的行数</param>
        /// <param name="sql">The original SQL select statement//原来的SQL select语句</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <param name="sqlCount">Outputs the SQL statement to query for the total number of matching rows//输出SQL语句以查询匹配行的总数</param>
        /// <param name="sqlPage">Outputs the SQL statement to retrieve a single page of matching rows//输出SQL语句以检索匹配行的单个页面</param>
        private void BuildPageQueries<T>(long skip, long take, string sql, ref object[] args, out string sqlCount, out string sqlPage)
        {
            // Add auto select clause--添加自动选择子句
            if (EnableAutoSelect)
                sql = AutoSelectHelper.AddSelectClause<T>(_provider, sql, _defaultMapper);

            // Split the SQL
            SQLParts parts;
            if (!Provider.PagingUtility.SplitSQL(sql, out parts))
                throw new Exception("Unable to parse SQL statement for paged query");

            sqlPage = _provider.BuildPageQuery(skip, take, parts, ref args);
            sqlCount = parts.SqlCount;
        }

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sqlCount">The SQL to retrieve the total number of records--SQL检索总记录数</param>
        /// <param name="countArgs">Arguments to any embedded parameters in the sqlCount statement--sqlCount语句中任何嵌入参数的参数</param>
        /// <param name="sqlPage">The SQL To retrieve a single page of results--SQL检索单个页面的结果</param>
        /// <param name="pageArgs">Arguments to any embedded parameters in the sqlPage statement--sqlPage语句中任何嵌入参数的参数</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     This method allows separate SQL statements to be explicitly provided for the two parts of the page query.
        ///     The page and itemsPerPage parameters are not used directly and are used simply to populate the returned Page object.
        ///     此方法允许为页面查询的两个部分显式提供单独的SQL语句。 页面和itemsPerPage参数不直接使用，仅用于填充返回的页面对象。
        /// </remarks>
        public Page<T> Page<T>(long page, long itemsPerPage, string sqlCount, object[] countArgs, string sqlPage, object[] pageArgs)
        {
            // Save the one-time command time out and use it for both queries--保存一次性命令超时，并将其用于这两个查询
            var saveTimeout = OneTimeCommandTimeout;

            // Setup the paged result--设置分页结果
            var result = new Page<T>
            {
                CurrentPage = page,
                ItemsPerPage = itemsPerPage,
                TotalItems = ExecuteScalar<long>(sqlCount, countArgs)
            };
            result.TotalPages = result.TotalItems / itemsPerPage;

            if ((result.TotalItems % itemsPerPage) != 0)
                result.TotalPages++;

            OneTimeCommandTimeout = saveTimeout;

            // Get the records--获取记录
            result.Items = Fetch<T>(sqlPage, pageArgs);

            // Done--完成
            return result;
        }

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">The base SQL query--基本SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.  It will also execute a second query to retrieve the total number of records in the result set.
        ///     PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。 它还将执行第二个查询来检索结果集中的记录总数。
        /// </remarks>
        public Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args)
        {
            string sqlCount, sqlPage;
            BuildPageQueries<T>((page - 1) * itemsPerPage, itemsPerPage, sql, ref args, out sqlCount, out sqlPage);
            return Page<T>(page, itemsPerPage, sqlCount, args, sqlPage, args);
        }

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.  It will also execute a second query to retrieve the total number of records in the result set.
        ///     PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。 它还将执行第二个查询来检索结果集中的记录总数。
        /// </remarks>
        public Page<T> Page<T>(long page, long itemsPerPage, Sql sql)
        {
            return Page<T>(page, itemsPerPage, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sqlCount">An SQL builder object representing the SQL to retrieve the total number of records--表示SQL的SQL构建器对象来检索总记录数</param>
        /// <param name="sqlPage">An SQL builder object representing the SQL to retrieve a single page of results--表示SQL的SQL构建器对象来检索单个页面的结果</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     This method allows separate SQL statements to be explicitly provided for the two parts of the page query.
        ///     The page and itemsPerPage parameters are not used directly and are used simply to populate the returned Page object.
        ///     此方法允许为页面查询的两个部分显式提供单独的SQL语句。 页面和itemsPerPage参数不直接使用，仅用于填充返回的页面对象。
        /// </remarks>
        public Page<T> Page<T>(long page, long itemsPerPage, Sql sqlCount, Sql sqlPage)
        {
            return Page<T>(page, itemsPerPage, sqlCount.SQL, sqlCount.Arguments, sqlPage.SQL, sqlPage.Arguments);
        }

        #endregion

        #region operation: Fetch (page)

        /// <summary>
        ///     Retrieves a page of records (without the total count)--检索一页记录（不计总数）
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">The base SQL query--基本SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.--PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。
        /// </remarks>
        public List<T> Fetch<T>(long page, long itemsPerPage, string sql, params object[] args)
        {
            return SkipTake<T>((page - 1) * itemsPerPage, itemsPerPage, sql, args);
        }

        /// <summary>
        ///     Retrieves a page of records (without the total count)--检索一页记录（不计总数）
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.--PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。
        /// </remarks>
        public List<T> Fetch<T>(long page, long itemsPerPage, Sql sql)
        {
            return SkipTake<T>((page - 1) * itemsPerPage, itemsPerPage, sql.SQL, sql.Arguments);
        }

        #endregion

        #region operation: SkipTake

        /// <summary>
        ///     Retrieves a range of records from result set--从结果集中检索一系列记录
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="skip">The number of rows at the start of the result set to skip over--结果集开始处的行数跳过</param>
        /// <param name="take">The number of rows to retrieve--要检索的行数</param>
        /// <param name="sql">The base SQL query--基本SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified range.--PetaPoco将自动修改提供的SELECT语句，仅检索指定范围区间的记录。
        /// </remarks>
        public List<T> SkipTake<T>(long skip, long take, string sql, params object[] args)
        {
            string sqlCount, sqlPage;
            BuildPageQueries<T>(skip, take, sql, ref args, out sqlCount, out sqlPage);
            return Fetch<T>(sqlPage, args);
        }

        /// <summary>
        ///     Retrieves a range of records from result set--从结果集中检索一系列记录
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="skip">The number of rows at the start of the result set to skip over--结果集开始处的行数跳过</param>
        /// <param name="take">The number of rows to retrieve--要检索的行数</param>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified range.--PetaPoco将自动修改提供的SELECT语句，仅检索指定范围区间的记录。
        /// </remarks>
        public List<T> SkipTake<T>(long skip, long take, Sql sql)
        {
            return SkipTake<T>(skip, take, sql.SQL, sql.Arguments);
        }

        #endregion

        #region operation: Query

        /// <summary>
        ///     Runs an SQL query, returning the results as an IEnumerable collection--运行SQL查询，将结果作为IEnumerable集合返回
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>An enumerable collection of result records--一个可枚举的结果记录集合</returns>
        /// <remarks>
        ///     For some DB providers, care should be taken to not start a new Query before finishing with
        ///     and disposing the previous one. In cases where this is an issue, consider using Fetch which returns the results as a List rather than an IEnumerable.
        ///     对于某些DB提供商，在完成和处理前一个DB提供程序之前，应小心不要启动新的查询。 在这是一个问题的情况下，请考虑使用Fetch返回结果作为列表而不是IEnumerable。
        /// </remarks>
        public IEnumerable<T> Query<T>(string sql, params object[] args)
        {
            if (EnableAutoSelect)
                sql = AutoSelectHelper.AddSelectClause<T>(_provider, sql, _defaultMapper);

            OpenSharedConnection();
            try
            {
                using (var cmd = CreateCommand(_sharedConnection, sql, args))
                {
                    IDataReader r;
                    var pd = PocoData.ForType(typeof(T), _defaultMapper);
                    try
                    {
                        r = cmd.ExecuteReader();
                        OnExecutedCommand(cmd);
                    }
                    catch (Exception x)
                    {
                        if (OnException(x))
                            throw;
                        yield break;
                    }
                    var factory = pd.GetFactory(cmd.CommandText, _sharedConnection.ConnectionString, 0, r.FieldCount, r, _defaultMapper) as Func<IDataReader, T>;
                    using (r)
                    {
                        while (true)
                        {
                            T poco;
                            try
                            {
                                if (!r.Read())
                                    yield break;
                                poco = factory(r);
                            }
                            catch (Exception x)
                            {
                                if (OnException(x))
                                    throw;
                                yield break;
                            }

                            yield return poco;
                        }
                    }
                }
            }
            finally
            {
                CloseSharedConnection();
            }
        }

        /// <summary>
        ///     Runs an SQL query, returning the results as an IEnumerable collection--运行SQL查询，将结果作为IEnumerable集合返回
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>An enumerable collection of result records--一个可枚举的结果记录集合</returns>
        /// <remarks>
        ///     For some DB providers, care should be taken to not start a new Query before finishing with
        ///     and disposing the previous one. In cases where this is an issue, consider using Fetch which returns the results as a List rather than an IEnumerable.
        ///     对于某些DB提供商，在完成和处理前一个DB提供程序之前，应小心不要启动新的查询。 在这是一个问题的情况下，请考虑使用Fetch返回结果作为列表而不是IEnumerable。
        /// </remarks>
        public IEnumerable<T> Query<T>(Sql sql)
        {
            return Query<T>(sql.SQL, sql.Arguments);
        }

        #endregion

        #region operation: Exists

        /// <summary>
        ///     Checks for the existence of a row matching the specified condition--检查是否存在符合指定条件的行
        /// </summary>
        /// <typeparam name="T">The Type representing the table being queried--表示要查询的表的类型</typeparam>
        /// <param name="sqlCondition">The SQL expression to be tested for (ie: the WHERE expression)--要测试的SQL表达式（即：WHERE表达式）</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>True if a record matching the condition is found.--如果找到符合条件的记录，则返回true。</returns>
        public bool Exists<T>(string sqlCondition, params object[] args)
        {
            var poco = PocoData.ForType(typeof(T), _defaultMapper).TableInfo;

            if (sqlCondition.TrimStart().StartsWith("where", StringComparison.OrdinalIgnoreCase))
                sqlCondition = sqlCondition.TrimStart().Substring(5);

            return ExecuteScalar<int>(string.Format(_provider.GetExistsSql(), Provider.EscapeTableName(poco.TableName), sqlCondition), args) != 0;
        }

        /// <summary>
        ///     Checks for the existence of a row with the specified primary key value.--检查是否存在具有指定主键值的行。
        /// </summary>
        /// <typeparam name="T">The Type representing the table being queried--表示要查询的表的类型</typeparam>
        /// <param name="primaryKey">The primary key value to look for--要查找的主键值</param>
        /// <returns>True if a record with the specified primary key value exists.--如果存在具有指定主键值的记录，则为真。</returns>
        public bool Exists<T>(object primaryKey)
        {
            return Exists<T>(string.Format("{0}=@0", _provider.EscapeSqlIdentifier(PocoData.ForType(typeof(T), _defaultMapper).TableInfo.PrimaryKey)), primaryKey);
        }

        #endregion

        #region operation: linq style (Exists, Single, SingleOrDefault etc...)

        /// <summary>
        ///     Returns the record with the specified primary key value--返回具有指定主键值的记录
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="primaryKey">The primary key value of the record to fetch--要获取的记录的主键值</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     Throws an exception if there are zero or more than one record with the specified primary key value.--如果具有指定主键值的零个或多个记录，则抛出异常。
        /// </remarks>
        public T Single<T>(object primaryKey)
        {
            return Single<T>(string.Format("WHERE {0}=@0", _provider.EscapeSqlIdentifier(PocoData.ForType(typeof(T), _defaultMapper).TableInfo.PrimaryKey)), primaryKey);
        }

        /// <summary>
        ///     Returns the record with the specified primary key value, or the default value if not found--返回具有指定主键值的记录，如果未找到则返回默认值
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="primaryKey">The primary key value of the record to fetch--要获取的记录的主键值</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     If there are no records with the specified primary key value, default(T) (typically null) is returned.--如果没有指定主键值的记录，则返回default（T）（通常为null）。
        /// </remarks>
        public T SingleOrDefault<T>(object primaryKey)
        {
            return SingleOrDefault<T>(string.Format("WHERE {0}=@0", _provider.EscapeSqlIdentifier(PocoData.ForType(typeof(T), _defaultMapper).TableInfo.PrimaryKey)), primaryKey);
        }

        /// <summary>
        ///     Runs a query that should always return a single row.--运行一个应该总是返回一行的查询。
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     Throws an exception if there are zero or more than one matching record--如果存在零个或多个匹配记录，则抛出异常
        /// </remarks>
        public T Single<T>(string sql, params object[] args)
        {
            return Query<T>(sql, args).Single();
        }

        /// <summary>
        ///     Runs a query that should always return either a single row, or no rows--运行一个应该总是返回单行或没有行的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The single record matching the specified primary key value, or default(T) if no matching rows--单个记录匹配指定的主键值，如果没有匹配的行，则为默认（T）</returns>
        public T SingleOrDefault<T>(string sql, params object[] args)
        {
            return Query<T>(sql, args).SingleOrDefault();
        }

        /// <summary>
        ///     Runs a query that should always return at least one return--运行一个应该总是返回至少一个返回的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The first record in the result set--结果集中的第一个记录</returns>
        public T First<T>(string sql, params object[] args)
        {
            return Query<T>(sql, args).First();
        }

        /// <summary>
        ///     Runs a query and returns the first record, or the default value if no matching records--运行查询并返回第一条记录，如果没有匹配的记录，则返回默认值
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The first record in the result set, or default(T) if no matching rows--结果集中的第一条记录，如果没有匹配行，则为默认（T）</returns>
        public T FirstOrDefault<T>(string sql, params object[] args)
        {
            return Query<T>(sql, args).FirstOrDefault();
        }

        /// <summary>
        ///     Runs a query that should always return a single row.--运行一个应该总是返回一行的查询。
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     Throws an exception if there are zero or more than one matching record--如果存在零个或多个匹配记录，则抛出异常
        /// </remarks>
        public T Single<T>(Sql sql)
        {
            return Query<T>(sql).Single();
        }

        /// <summary>
        ///     Runs a query that should always return either a single row, or no rows--运行一个应该总是返回单行或没有行的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The single record matching the specified primary key value, or default(T) if no matching rows--单个记录匹配指定的主键值，如果没有匹配的行，则为默认（T）</returns>
        public T SingleOrDefault<T>(Sql sql)
        {
            return Query<T>(sql).SingleOrDefault();
        }

        /// <summary>
        ///     Runs a query that should always return at least one return--运行一个应该总是返回至少一个返回的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The first record in the result set--结果集中的第一个记录</returns>
        public T First<T>(Sql sql)
        {
            return Query<T>(sql).First();
        }

        /// <summary>
        ///     Runs a query and returns the first record, or the default value if no matching records--运行查询并返回第一条记录，如果没有匹配的记录，则返回默认值
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The first record in the result set, or default(T) if no matching rows--结果集中的第一条记录，如果没有匹配行，则为默认（T）</returns>
        public T FirstOrDefault<T>(Sql sql)
        {
            return Query<T>(sql).FirstOrDefault();
        }

        #endregion

        #region operation: Insert
        /// <summary>
        /// Bulk inserts multiple rows to SQL
        /// </summary>
        /// <param name="tableName">The name of the table to insert into</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table</param>
        /// <param name="autoIncrement">True if the primary key is automatically allocated by the DB</param>
        /// <param name="pocos">The POCO objects that specifies the column values to be inserted</param>
        /// <param name="batchSize">The number of POCOS to be grouped together for each database rounddtrip</param>        
        public void BulkInsert(string tableName, string primaryKeyName, bool autoIncrement, IEnumerable<object> pocos, int batchSize = 25)
        {
            try
            {
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, ""))
                    {
                        var pd = PocoData.ForObject(pocos.First(), primaryKeyName, _defaultMapper);
                        // Create list of columnnames only once
                        var names = new List<string>();
                        foreach (var i in pd.Columns)
                        {
                            // Don't insert result columns
                            if (i.Value.ResultColumn)
                                continue;

                            // Don't insert the primary key (except under oracle where we need bring in the next sequence value)
                            if (autoIncrement && primaryKeyName != null && string.Compare(i.Key, primaryKeyName, true) == 0)
                            {
                                // Setup auto increment expression
                                string autoIncExpression = _provider.GetAutoIncrementExpression(pd.TableInfo);
                                if (autoIncExpression != null)
                                {
                                    names.Add(i.Key);
                                }
                                continue;
                            }
                            names.Add(_provider.EscapeSqlIdentifier(i.Key));
                        }
                        var namesArray = names.ToArray();

                        var values = new List<string>();
                        int count = 0;
                        do
                        {
                            cmd.CommandText = "";
                            cmd.Parameters.Clear();
                            var index = 0;
                            foreach (var poco in pocos.Skip(count).Take(batchSize))
                            {
                                values.Clear();
                                foreach (var i in pd.Columns)
                                {
                                    // Don't insert result columns
                                    if (i.Value.ResultColumn) continue;

                                    // Don't insert the primary key (except under oracle where we need bring in the next sequence value)
                                    if (autoIncrement && primaryKeyName != null && string.Compare(i.Key, primaryKeyName, true) == 0)
                                    {
                                        // Setup auto increment expression
                                        string autoIncExpression = _provider.GetAutoIncrementExpression(pd.TableInfo);
                                        if (autoIncExpression != null)
                                        {
                                            values.Add(autoIncExpression);
                                        }
                                        continue;
                                    }

                                    values.Add(string.Format("{0}{1}", _paramPrefix, index++));
                                    AddParam(cmd, i.Value.GetValue(poco), i.Value.PropertyInfo);
                                }

                                string outputClause = String.Empty;
                                if (autoIncrement)
                                {
                                    outputClause = _provider.GetInsertOutputClause(primaryKeyName);
                                }

                                cmd.CommandText += string.Format("INSERT INTO {0} ({1}){2} VALUES ({3})", _provider.EscapeTableName(tableName),
                                                                 string.Join(",", namesArray), outputClause, string.Join(",", values.ToArray()));
                            }
                            // Are we done?
                            if (cmd.CommandText == "") break;
                            count += batchSize;
                            DoPreExecute(cmd);
                            cmd.ExecuteNonQuery();
                            OnExecutedCommand(cmd);
                        }
                        while (true);

                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
            catch (Exception x)
            {
                if (OnException(x))
                    throw;
            }
        }


        /// <summary>
        /// Performs a SQL Bulk Insert
        /// </summary>
        /// <param name="pocos">The POCO objects that specifies the column values to be inserted</param>        
        /// <param name="batchSize">The number of POCOS to be grouped together for each database rounddtrip</param>        
        public void BulkInsert(IEnumerable<object> pocos, int batchSize = 25)
        {
            if (!pocos.Any()) return;
            var pd = PocoData.ForType(pocos.First().GetType(), _defaultMapper);
            BulkInsert(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, pd.TableInfo.AutoIncrement, pocos);
        }

        /// <summary>
        ///     Performs an SQL Insert--执行SQL插入
        /// </summary>
        /// <param name="tableName">The name of the table to insert into--要插入的表的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be inserted--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables--新记录的自动分配主键，或非自动递增表的空值</returns>
        public object Insert(string tableName, object poco)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new ArgumentNullException("tableName");

            if (poco == null)
                throw new ArgumentNullException("poco");

            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);

            return ExecuteInsert(tableName, pd == null ? null : pd.TableInfo.PrimaryKey, pd != null && pd.TableInfo.AutoIncrement, poco);
        }

        /// <summary>
        ///     Performs an SQL Insert--执行SQL插入
        /// </summary>
        /// <param name="tableName">The name of the table to insert into--要插入的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be inserted--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables--新记录的自动分配主键，或非自动递增表的空值</returns>
        public object Insert(string tableName, string primaryKeyName, object poco)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new ArgumentNullException("tableName");

            if (string.IsNullOrEmpty(primaryKeyName))
                throw new ArgumentNullException("primaryKeyName");

            if (poco == null)
                throw new ArgumentNullException("poco");

            var t = poco.GetType();
            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);
            var autoIncrement = pd == null || pd.TableInfo.AutoIncrement ||
                                t.Name.Contains("AnonymousType") &&
                                !t.GetProperties().Any(p => p.Name.Equals(primaryKeyName, StringComparison.OrdinalIgnoreCase));

            return ExecuteInsert(tableName, primaryKeyName, autoIncrement, poco);
        }

        /// <summary>
        ///     Performs an SQL Insert--执行SQL插入
        /// </summary>
        /// <param name="tableName">The name of the table to insert into--要插入的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="autoIncrement">True if the primary key is automatically allocated by the DB--如果主键由数据库自动分配，则为真</param>
        /// <param name="poco">The POCO object that specifies the column values to be inserted--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables--新记录的自动分配主键，或非自动递增表的空值</returns>
        /// <remarks>
        ///     Inserts a poco into a table.  If the poco has a property with the same name
        ///     as the primary key the id of the new record is assigned to it.  Either way,the new id is returned.
        ///     将poco插入table。 如果poco具有与主键名称相同的属性，则将其分配给新记录的ID。 无论哪种方式，都会返回新的ID。
        /// </remarks>
        public object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new ArgumentNullException("tableName");

            if (string.IsNullOrEmpty(primaryKeyName))
                throw new ArgumentNullException("primaryKeyName");

            if (poco == null)
                throw new ArgumentNullException("poco");

            return ExecuteInsert(tableName, primaryKeyName, autoIncrement, poco);
        }

        /// <summary>
        ///     Performs an SQL Insert--执行SQL插入
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be inserted--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables--新记录的自动分配主键，或非自动递增表的空值</returns>
        /// <remarks>
        ///     The name of the table, it's primary key and whether it's an auto-allocated primary key are retrieved
        ///     from the POCO's attributes--表的名称，它是主键，是否是自动分配的主键，从POCO的属性中检索
        /// </remarks>
        public object Insert(object poco)
        {
            if (poco == null)
                throw new ArgumentNullException("poco");

            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);
            return ExecuteInsert(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, pd.TableInfo.AutoIncrement, poco);
        }

        private object ExecuteInsert(string tableName, string primaryKeyName, bool autoIncrement, object poco)
        {
            try
            {
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, ""))
                    {
                        var pd = PocoData.ForObject(poco, primaryKeyName, _defaultMapper);
                        var names = new List<string>();
                        var values = new List<string>();
                        var index = 0;
                        foreach (var i in pd.Columns)
                        {
                            // Don't insert result columns--不要插入结果列
                            if (i.Value.ResultColumn)
                                continue;

                            // Don't insert the primary key (except under oracle where we need bring in the next sequence value)
                            //不要插入主键（除了oracle之外，我们需要带来下一个序列值）
                            if (autoIncrement && primaryKeyName != null && string.Compare(i.Key, primaryKeyName, true) == 0)
                            {
                                // Setup auto increment expression--设置自动递增表达式
                                string autoIncExpression = _provider.GetAutoIncrementExpression(pd.TableInfo);
                                if (autoIncExpression != null)
                                {
                                    names.Add(i.Key);
                                    values.Add(autoIncExpression);
                                }
                                continue;
                            }

                            names.Add(_provider.EscapeSqlIdentifier(i.Key));
                            values.Add(string.Format(i.Value.InsertTemplate ?? "{0}{1}", _paramPrefix, index++));
                            AddParam(cmd, i.Value.GetValue(poco), i.Value.PropertyInfo);
                        }

                        string outputClause = String.Empty;
                        if (autoIncrement)
                        {
                            outputClause = _provider.GetInsertOutputClause(primaryKeyName);
                        }

                        cmd.CommandText = string.Format("INSERT INTO {0} ({1}){2} VALUES ({3})",
                            _provider.EscapeTableName(tableName),
                            string.Join(",", names.ToArray()),
                            outputClause,
                            string.Join(",", values.ToArray())
                            );

                        if (!autoIncrement)
                        {
                            DoPreExecute(cmd);
                            cmd.ExecuteNonQuery();
                            OnExecutedCommand(cmd);

                            PocoColumn pkColumn;
                            if (primaryKeyName != null && pd.Columns.TryGetValue(primaryKeyName, out pkColumn))
                                return pkColumn.GetValue(poco);
                            else
                                return null;
                        }

                        object id = _provider.ExecuteInsert(this, cmd, primaryKeyName);

                        // Assign the ID back to the primary key property--将ID分配回主键属性
                        if (primaryKeyName != null && !poco.GetType().Name.Contains("AnonymousType"))
                        {
                            PocoColumn pc;
                            if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                            {
                                pc.SetValue(poco, pc.ChangeType(id));
                            }
                        }

                        return id;
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
            catch (Exception x)
            {
                if (OnException(x))
                    throw;
                return null;
            }
        }

        #endregion

        #region operation: Update

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <returns>The number of affected records--受影响记录的数量</returns>
        public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new ArgumentNullException("tableName");

            if (string.IsNullOrEmpty(primaryKeyName))
                throw new ArgumentNullException("primaryKeyName");

            if (poco == null)
                throw new ArgumentNullException("poco");

            return ExecuteUpdate(tableName, primaryKeyName, poco, primaryKeyValue, null);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new ArgumentNullException("tableName");

            if (string.IsNullOrEmpty(primaryKeyName))
                throw new ArgumentNullException("primaryKeyName");

            if (poco == null)
                throw new ArgumentNullException("poco");

            return ExecuteUpdate(tableName, primaryKeyName, poco, primaryKeyValue, columns);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(string tableName, string primaryKeyName, object poco)
        {
            return Update(tableName, primaryKeyName, poco, null);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns)
        {
            if (string.IsNullOrEmpty(tableName))
                throw new ArgumentNullException("tableName");

            if (string.IsNullOrEmpty(primaryKeyName))
                throw new ArgumentNullException("primaryKeyName");

            if (poco == null)
                throw new ArgumentNullException("poco");

            return ExecuteUpdate(tableName, primaryKeyName, poco, null, columns);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(object poco, IEnumerable<string> columns)
        {
            return Update(poco, null, columns);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(object poco)
        {
            return Update(poco, null, null);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(object poco, object primaryKeyValue)
        {
            return Update(poco, primaryKeyValue, null);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update(object poco, object primaryKeyValue, IEnumerable<string> columns)
        {
            if (poco == null)
                throw new ArgumentNullException("poco");

            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);
            return ExecuteUpdate(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco, primaryKeyValue, columns);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to update--POCO类的属性指定要更新的表的名称</typeparam>
        /// <param name="sql">The SQL update and condition clause (ie: everything after "UPDATE tablename"--SQL更新和条件子句（即：“UPDATE tablename”之后的所有内容）</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update<T>(string sql, params object[] args)
        {
            if (string.IsNullOrEmpty(sql))
                throw new ArgumentNullException("sql");

            var pd = PocoData.ForType(typeof(T), _defaultMapper);
            return Execute(string.Format("UPDATE {0} {1}", _provider.EscapeTableName(pd.TableInfo.TableName), sql), args);
        }

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to update--POCO类的属性指定要更新的表的名称</typeparam>
        /// <param name="sql">
        ///     An SQL builder object representing the SQL update and condition clause (ie: everything after "UPDATE
        ///     tablename"--表示SQL更新和条件子句的SQL构建器对象（即：“UPDATE tablename”之后的所有内容）
        /// </param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Update<T>(Sql sql)
        {
            if (sql == null)
                throw new ArgumentNullException("sql");

            var pd = PocoData.ForType(typeof(T), _defaultMapper);
            return Execute(new Sql(string.Format("UPDATE {0}", _provider.EscapeTableName(pd.TableInfo.TableName))).Append(sql));
        }

        private int ExecuteUpdate(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns)
        {
            try
            {
                OpenSharedConnection();
                try
                {
                    using (var cmd = CreateCommand(_sharedConnection, ""))
                    {
                        var sb = new StringBuilder();
                        var index = 0;
                        var pd = PocoData.ForObject(poco, primaryKeyName, _defaultMapper);
                        if (columns == null)
                        {
                            foreach (var i in pd.Columns)
                            {
                                // Don't update the primary key, but grab the value if we don't have it--不要更新主键，但是如果没有它，可以获取该值
                                if (string.Compare(i.Key, primaryKeyName, true) == 0)
                                {
                                    if (primaryKeyValue == null)
                                        primaryKeyValue = i.Value.GetValue(poco);
                                    continue;
                                }

                                // Dont update result only columns--不更新结果列
                                if (i.Value.ResultColumn)
                                    continue;

                                // Build the sql--构建sql
                                if (index > 0)
                                    sb.Append(", ");
                                sb.AppendFormat(i.Value.UpdateTemplate ?? "{0} = {1}{2}", _provider.EscapeSqlIdentifier(i.Key), _paramPrefix, index++);

                                // Store the parameter in the command--将参数存储在命令中
                                AddParam(cmd, i.Value.GetValue(poco), i.Value.PropertyInfo);
                            }
                        }
                        else
                        {
                            foreach (var colname in columns)
                            {
                                var pc = pd.Columns[colname];

                                // Build the sql--构建sql
                                if (index > 0)
                                    sb.Append(", ");
                                sb.AppendFormat(pc.UpdateTemplate ?? "{0} = {1}{2}", _provider.EscapeSqlIdentifier(colname), _paramPrefix, index++);

                                // Store the parameter in the command--将参数存储在命令中
                                AddParam(cmd, pc.GetValue(poco), pc.PropertyInfo);
                            }

                            // Grab primary key value--抓主键值
                            if (primaryKeyValue == null)
                            {
                                var pc = pd.Columns[primaryKeyName];
                                primaryKeyValue = pc.GetValue(poco);
                            }
                        }

                        // Find the property info for the primary key--查找主键的属性信息
                        PropertyInfo pkpi = null;
                        if (primaryKeyName != null)
                        {
                            PocoColumn col;
                            pkpi = pd.Columns.TryGetValue(primaryKeyName, out col)
                                ? col.PropertyInfo
                                : new { Id = primaryKeyValue }.GetType().GetProperties()[0];
                        }

                        cmd.CommandText = string.Format("UPDATE {0} SET {1} WHERE {2} = {3}{4}",
                            _provider.EscapeTableName(tableName), sb.ToString(), _provider.EscapeSqlIdentifier(primaryKeyName), _paramPrefix, index++);
                        AddParam(cmd, primaryKeyValue, pkpi);

                        DoPreExecute(cmd);

                        // Do it
                        var retv = cmd.ExecuteNonQuery();
                        OnExecutedCommand(cmd);
                        return retv;
                    }
                }
                finally
                {
                    CloseSharedConnection();
                }
            }
            catch (Exception x)
            {
                if (OnException(x))
                    throw;
                return -1;
            }
        }

        #endregion

        #region operation: Delete

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <param name="tableName">The name of the table to delete from--要删除的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">The POCO object whose primary key value will be used to delete the row--主键值将用于删除行的POCO对象</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        public int Delete(string tableName, string primaryKeyName, object poco)
        {
            return Delete(tableName, primaryKeyName, poco, null);
        }

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <param name="tableName">The name of the table to delete from--要删除的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">
        ///     The POCO object whose primary key value will be used to delete the row (or null to use the supplied
        ///     primary key value)--主键值将用于删除行的POCO对象（或使用提供的主键值的空值）
        /// </param>
        /// <param name="primaryKeyValue">
        ///     The value of the primary key identifing the record to be deleted (or null, or get this
        ///     value from the POCO instance)--识别要删除的记录的主键的值（或null，或从POCO实例获取该值）
        /// </param>
        /// <returns>The number of rows affected受影响的行数</returns>
        public int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue)
        {
            // If primary key value not specified, pick it up from the object--如果未指定主键值，请从对象中取出
            if (primaryKeyValue == null)
            {
                var pd = PocoData.ForObject(poco, primaryKeyName, _defaultMapper);
                PocoColumn pc;
                if (pd.Columns.TryGetValue(primaryKeyName, out pc))
                {
                    primaryKeyValue = pc.GetValue(poco);
                }
            }

            // Do it
            var sql = string.Format("DELETE FROM {0} WHERE {1}=@0", _provider.EscapeTableName(tableName), _provider.EscapeSqlIdentifier(primaryKeyName));
            return Execute(sql, primaryKeyValue);
        }

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <param name="poco">The POCO object specifying the table name and primary key value of the row to be deleted--指定要删除的行的表名和主键值的POCO对象</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        public int Delete(object poco)
        {
            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);
            return Delete(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco);
        }

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <typeparam name="T">The POCO class whose attributes identify the table and primary key to be used in the delete--POCO类的属性标识要删除的表和主键</typeparam>
        /// <param name="pocoOrPrimaryKey">The value of the primary key of the row to delete--要删除的行的主键的值</param>
        /// <returns></returns>
        public int Delete<T>(object pocoOrPrimaryKey)
        {
            if (pocoOrPrimaryKey.GetType() == typeof(T))
                return Delete(pocoOrPrimaryKey);

            var pd = PocoData.ForType(typeof(T), _defaultMapper);

            if (pocoOrPrimaryKey.GetType().Name.Contains("AnonymousType"))
            {
                var pi = pocoOrPrimaryKey.GetType().GetProperty(pd.TableInfo.PrimaryKey);

                if (pi == null)
                    throw new InvalidOperationException(string.Format("Anonymous type does not contain an id for PK column `{0}`.", pd.TableInfo.PrimaryKey));

                pocoOrPrimaryKey = pi.GetValue(pocoOrPrimaryKey, new object[0]);
            }

            return Delete(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, null, pocoOrPrimaryKey);
        }

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to delete from--POCO类的属性指定要从中删除的表的名称</typeparam>
        /// <param name="sql">The SQL condition clause identifying the row to delete (ie: everything after "DELETE FROM tablename"--标识要删除的行的SQL条件子句（即：“DELETE FROM tablename”之后的所有内容）</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Delete<T>(string sql, params object[] args)
        {
            var pd = PocoData.ForType(typeof(T), _defaultMapper);
            return Execute(string.Format("DELETE FROM {0} {1}", _provider.EscapeTableName(pd.TableInfo.TableName), sql), args);
        }

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to delete from--POCO类的属性指定要从中删除的表的名称</typeparam>
        /// <param name="sql">
        ///     An SQL builder object representing the SQL condition clause identifying the row to delete (ie:
        ///     everything after "UPDATE tablename"--表示SQL条件子句的SQL构建器对象，用于标识要删除的行（即：“UPDATE tablename”之后的所有内容）
        /// </param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        public int Delete<T>(Sql sql)
        {
            var pd = PocoData.ForType(typeof(T), _defaultMapper);
            return Execute(new Sql(string.Format("DELETE FROM {0}", _provider.EscapeTableName(pd.TableInfo.TableName))).Append(sql));
        }

        #endregion

        #region operation: IsNew

        /// <summary>
        ///     Check if a poco represents a new row--检查poco是否代表新行
        /// </summary>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">The object instance whose "newness" is to be tested--要测试其“新行”的对象实例</param>
        /// <returns>True if the POCO represents a record already in the database--如果POCO表示已经在数据库中的记录，则为真</returns>
        /// <remarks>This method simply tests if the POCO's primary key column property has been set to something non-zero.--该方法只是测试POCO的主键列属性是否被设置为非零。</remarks>
        public bool IsNew(string primaryKeyName, object poco)
        {
            if (poco == null)
                throw new ArgumentNullException("poco");

            if (string.IsNullOrEmpty(primaryKeyName))
                throw new ArgumentException("primaryKeyName");

            return IsNew(primaryKeyName, PocoData.ForObject(poco, primaryKeyName, _defaultMapper), poco);
        }

        protected virtual bool IsNew(string primaryKeyName, PocoData pd, object poco)
        {
            if (string.IsNullOrEmpty(primaryKeyName) || poco is ExpandoObject)
                throw new InvalidOperationException("IsNew() and Save() are only supported on tables with identity (inc auto-increment) primary key columns");

            object pk;
            PocoColumn pc;
            PropertyInfo pi;
            if (pd.Columns.TryGetValue(primaryKeyName, out pc))
            {
                pk = pc.GetValue(poco);
                pi = pc.PropertyInfo;
            }
            else
            {
                pi = poco.GetType().GetProperty(primaryKeyName);
                if (pi == null)
                    throw new ArgumentException(string.Format("The object doesn't have a property matching the primary key column name '{0}'", primaryKeyName));
                pk = pi.GetValue(poco, null);
            }

            var type = pk != null ? pk.GetType() : pi.PropertyType;

            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) || !type.IsValueType)
                return pk == null;

            if (type == typeof(string))
                return string.IsNullOrEmpty((string)pk);
            if (!pi.PropertyType.IsValueType)
                return pk == null;
            if (type == typeof(long))
                return (long)pk == default(long);
            if (type == typeof(int))
                return (int)pk == default(int);
            if (type == typeof(Guid))
                return (Guid)pk == default(Guid);
            if (type == typeof(ulong))
                return (ulong)pk == default(ulong);
            if (type == typeof(uint))
                return (uint)pk == default(uint);
            if (type == typeof(short))
                return (short)pk == default(short);
            if (type == typeof(ushort))
                return (ushort)pk == default(ushort);

            // Create a default instance and compare--创建一个默认实例并进行比较
            return pk == Activator.CreateInstance(pk.GetType());
        }

        /// <summary>
        ///     Check if a poco represents a new row--检查poco是否代表新行
        /// </summary>
        /// <param name="poco">The object instance whose "newness" is to be tested--要测试其“新行”的对象实例</param>
        /// <returns>True if the POCO represents a record already in the database--如果POCO表示已经在数据库中的记录，则为真</returns>
        /// <remarks>This method simply tests if the POCO's primary key column property has been set to something non-zero.--该方法只是测试POCO的主键列属性是否被设置为非零。</remarks>
        public bool IsNew(object poco)
        {
            if (poco == null)
                throw new ArgumentNullException("poco");

            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);
            return IsNew(pd.TableInfo.PrimaryKey, pd, poco);
        }

        #endregion

        #region operation: Save

        /// <summary>
        ///     Saves a POCO by either performing either an SQL Insert or SQL Update--通过执行SQL插入或SQL更新来保存POCO
        /// </summary>
        /// <param name="tableName">The name of the table to be updated--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">The POCO object to be saved--要保存的POCO对象</param>
        public void Save(string tableName, string primaryKeyName, object poco)
        {
            if (IsNew(primaryKeyName, poco))
            {
                Insert(tableName, primaryKeyName, true, poco);
            }
            else
            {
                Update(tableName, primaryKeyName, poco);
            }
        }

        /// <summary>
        ///     Saves a POCO by either performing either an SQL Insert or SQL Update--通过执行SQL插入或SQL更新来保存POCO
        /// </summary>
        /// <param name="poco">The POCO object to be saved--要保存的POCO对象</param>
        public void Save(object poco)
        {
            var pd = PocoData.ForType(poco.GetType(), _defaultMapper);
            Save(pd.TableInfo.TableName, pd.TableInfo.PrimaryKey, poco);
        }

        #endregion

        #region operation: Multi-Poco Query/Fetch

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args)
        {
            return Query<T1, T2, TRet>(cb, sql, args).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args)
        {
            return Query<T1, T2, T3, TRet>(cb, sql, args).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args)
        {
            return Query<T1, T2, T3, T4, TRet>(cb, sql, args).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args)
        {
            return Query<TRet>(new Type[] { typeof(T1), typeof(T2) }, cb, sql, args);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args)
        {
            return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql, args);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args)
        {
            return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql, args);
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql)
        {
            return Query<T1, T2, TRet>(cb, sql.SQL, sql.Arguments).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql)
        {
            return Query<T1, T2, T3, TRet>(cb, sql.SQL, sql.Arguments).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql)
        {
            return Query<T1, T2, T3, T4, TRet>(cb, sql.SQL, sql.Arguments).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql)
        {
            return Query<TRet>(new Type[] { typeof(T1), typeof(T2) }, cb, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql)
        {
            return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql)
        {
            return Query<TRet>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<T1> Fetch<T1, T2>(string sql, params object[] args)
        {
            return Query<T1, T2>(sql, args).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<T1> Fetch<T1, T2, T3>(string sql, params object[] args)
        {
            return Query<T1, T2, T3>(sql, args).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<T1> Fetch<T1, T2, T3, T4>(string sql, params object[] args)
        {
            return Query<T1, T2, T3, T4>(sql, args).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Query<T1, T2>(string sql, params object[] args)
        {
            return Query<T1>(new Type[] { typeof(T1), typeof(T2) }, null, sql, args);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Query<T1, T2, T3>(string sql, params object[] args)
        {
            return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql, args);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Query<T1, T2, T3, T4>(string sql, params object[] args)
        {
            return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql, args);
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<T1> Fetch<T1, T2>(Sql sql)
        {
            return Query<T1, T2>(sql.SQL, sql.Arguments).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<T1> Fetch<T1, T2, T3>(Sql sql)
        {
            return Query<T1, T2, T3>(sql.SQL, sql.Arguments).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        public List<T1> Fetch<T1, T2, T3, T4>(Sql sql)
        {
            return Query<T1, T2, T3, T4>(sql.SQL, sql.Arguments).ToList();
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Query<T1, T2>(Sql sql)
        {
            return Query<T1>(new Type[] { typeof(T1), typeof(T2) }, null, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Query<T1, T2, T3>(Sql sql)
        {
            return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql)
        {
            return Query<T1>(new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null, sql.SQL, sql.Arguments);
        }

        /// <summary>
        ///     Performs a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="types">An array of Types representing the POCO types of the returned result set.--一组表示返回结果集的POCO类型的类型。</param>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Query<TRet>(Type[] types, object cb, string sql, params object[] args)
        {
            OpenSharedConnection();
            try
            {
                using (var cmd = CreateCommand(_sharedConnection, sql, args))
                {
                    IDataReader r;
                    try
                    {
                        r = cmd.ExecuteReader();
                        OnExecutedCommand(cmd);
                    }
                    catch (Exception x)
                    {
                        if (OnException(x))
                            throw;
                        yield break;
                    }
                    var factory = MultiPocoFactory.GetFactory<TRet>(types, _sharedConnection.ConnectionString, sql, r, _defaultMapper);
                    if (cb == null)
                        cb = MultiPocoFactory.GetAutoMapper(types.ToArray());
                    bool bNeedTerminator = false;
                    using (r)
                    {
                        while (true)
                        {
                            TRet poco;
                            try
                            {
                                if (!r.Read())
                                    break;
                                poco = factory(r, cb);
                            }
                            catch (Exception x)
                            {
                                if (OnException(x))
                                    throw;
                                yield break;
                            }

                            if (poco != null)
                                yield return poco;
                            else
                                bNeedTerminator = true;
                        }
                        if (bNeedTerminator)
                        {
                            var poco = (TRet)(cb as Delegate).DynamicInvoke(new object[types.Length]);
                            if (poco != null)
                                yield return poco;
                            else
                                yield break;
                        }
                    }
                }
            }
            finally
            {
                CloseSharedConnection();
            }
        }

        #endregion

        #region operation: Multi-Result Set
        /// <summary>
        /// Perform a multi-results set query--执行多结果集查询
        /// </summary>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A GridReader to be queried--要查询的GridReader</returns>
        public IGridReader QueryMultiple(Sql sql)
        {
            return QueryMultiple(sql.SQL, sql.Arguments);
        }

        /// <summary>
        /// Perform a multi-results set query--执行多结果集查询
        /// </summary>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A GridReader to be queried--要查询的GridReader</returns>
        public IGridReader QueryMultiple(string sql, params object[] args)
        {
            OpenSharedConnection();

            GridReader result = null;

            var cmd = CreateCommand(_sharedConnection, sql, args);

            try
            {
                var reader = cmd.ExecuteReader();
                result = new GridReader(this, cmd, reader, _defaultMapper);
            }
            catch (Exception x)
            {
                if (OnException(x))
                    throw;
            }
            return result;
        }
        #endregion

        #region Last Command

        /// <summary>
        ///     Retrieves the SQL of the last executed statement--检索最后执行的语句的SQL
        /// </summary>
        public string LastSQL
        {
            get { return _lastSql; }
        }

        /// <summary>
        ///     Retrieves the arguments to the last execute statement--检索最后一个execute语句的参数
        /// </summary>
        public object[] LastArgs
        {
            get { return _lastArgs; }
        }

        /// <summary>
        ///     Returns a formatted string describing the last executed SQL statement and it's argument values--返回一个格式化的字符串，描述最后执行的SQL语句及其参数值
        /// </summary>
        public string LastCommand
        {
            get { return FormatCommand(_lastSql, _lastArgs); }
        }

        #endregion

        #region FormatCommand

        /// <summary>
        ///     Formats the contents of a DB command for display--格式化DB命令的内容进行显示
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        public string FormatCommand(IDbCommand cmd)
        {
            return FormatCommand(cmd.CommandText, (from IDataParameter parameter in cmd.Parameters select parameter.Value).ToArray());
        }

        /// <summary>
        ///     Formats an SQL query and it's arguments for display--格式化SQL查询，它是用于显示的参数
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public string FormatCommand(string sql, object[] args)
        {
            var sb = new StringBuilder();
            if (sql == null)
                return "";
            sb.Append(sql);
            if (args != null && args.Length > 0)
            {
                sb.Append("\n");
                for (int i = 0; i < args.Length; i++)
                {
                    sb.AppendFormat("\t -> {0}{1} [{2}] = \"{3}\"\n", _paramPrefix, i, args[i].GetType().Name, args[i]);
                }
                sb.Remove(sb.Length - 1, 1);
            }
            return sb.ToString();
        }

        #endregion

        #region Public Properties

        /// <summary>
        ///     Gets the default mapper.--获取默认的映射器。
        /// </summary>
        public IMapper DefaultMapper
        {
            get { return _defaultMapper; }
        }

        /// <summary>
        ///     When set to true, PetaPoco will automatically create the "SELECT columns" part of any query that looks like it needs it
        ///     当设置为true时，PetaPoco会自动创建任何看起来像需要的查询的“SELECT columns”部分
        /// </summary>
        public bool EnableAutoSelect { get; set; }

        /// <summary>
        ///     When set to true, parameters can be named ?myparam and populated from properties of the passed in argument values.--当设置为true时，参数可以命名为myparam并从传入的参数值的属性中填充。
        /// </summary>
        public bool EnableNamedParams { get; set; }

        /// <summary>
        ///     Sets the timeout value for all SQL statements.--设置所有SQL语句的超时值。
        /// </summary>
        public int CommandTimeout { get; set; }

        /// <summary>
        ///     Sets the timeout value for the next (and only next) SQL statement--设置下一个（而且只有下一个）SQL语句的超时值。
        /// </summary>
        public int OneTimeCommandTimeout { get; set; }

        /// <summary>
        ///     Gets the loaded database provider. <seealso cref="Provider" />.--获取加载的数据库提供程序。 <seealso cref =“Provider”/>。
        /// </summary>
        /// <returns>
        ///     The loaded database type.--加载的数据库类型。
        /// </returns>
        public IProvider Provider
        {
            get { return _provider; }
        }

        /// <summary>
        ///     Gets the connection string.--获取连接字符串。
        /// </summary>
        /// <returns>
        ///     The connection string.--连接字符串。
        /// </returns>
        public string ConnectionString
        {
            get { return _connectionString; }
        }

        /// <summary>
        ///     Gets or sets the transaction isolation level.--获取或设置事务隔离级别。
        /// </summary>
        /// <remarks>
        ///     When value is null, the underlying providers default isolation level is used.--当值为null时，使用底层提供程序默认隔离级别。
        /// </remarks>
        public IsolationLevel? IsolationLevel
        {
            get { return _isolationLevel; }
            set
            {
                if (_transaction != null)
                    throw new InvalidOperationException("Isolation level can't be changed during a transaction.");

                _isolationLevel = value;
            }
        }

        #endregion

        #region Member Fields

        // Member variables
        private IMapper _defaultMapper;
        private string _connectionString;
        private IProvider _provider;
        private IDbConnection _sharedConnection;
        private IDbTransaction _transaction;
        private int _sharedConnectionDepth;
        private int _transactionDepth;
        private bool _transactionCancelled;
        private string _lastSql;
        private object[] _lastArgs;
        private string _paramPrefix;
        private DbProviderFactory _factory;
        private IsolationLevel? _isolationLevel;

        #endregion

        #region Internal operations

        internal void ExecuteNonQueryHelper(IDbCommand cmd)
        {
            DoPreExecute(cmd);
            cmd.ExecuteNonQuery();
            OnExecutedCommand(cmd);
        }

        internal object ExecuteScalarHelper(IDbCommand cmd)
        {
            DoPreExecute(cmd);
            object r = cmd.ExecuteScalar();
            OnExecutedCommand(cmd);
            return r;
        }

        internal void DoPreExecute(IDbCommand cmd)
        {
            // Setup command timeout--设置命令超时
            if (OneTimeCommandTimeout != 0)
            {
                cmd.CommandTimeout = OneTimeCommandTimeout;
                OneTimeCommandTimeout = 0;
            }
            else if (CommandTimeout != 0)
            {
                cmd.CommandTimeout = CommandTimeout;
            }

            // Call hook--调用挂钩--在执行DB命令之前调用
            OnExecutingCommand(cmd);

            // Save it
            _lastSql = cmd.CommandText;
            _lastArgs = (from IDataParameter parameter in cmd.Parameters select parameter.Value).ToArray();
        }

        #endregion
    }


    /// <summary>
    ///     A helper class which enables fluent configuration.--一个帮助类可以流畅配置。
    /// </summary>
    public class DatabaseConfiguration : IDatabaseBuildConfiguration, IBuildConfigurationSettings, IHideObjectMethods
    {
        private readonly IDictionary<string, object> _settings = new Dictionary<string, object>();

        /// <summary>
        ///     Private constructor to force usage of static build method.--私有构造函数强制使用静态构建方法。
        /// </summary>
        private DatabaseConfiguration()
        {
        }

        void IBuildConfigurationSettings.SetSetting(string key, object value)
        {
            // Note: no argument checking because, pref, enduser unlikely and handled by RT/FW--注意：没有参数检查，因为pref，enduser不太可能，由RT / FW处理
            if (value != null)
                _settings[key] = value;
            else
                _settings.Remove(key);
        }

        void IBuildConfigurationSettings.TryGetSetting<T>(string key, Action<T> setSetting, Action onFail = null)
        {
            // Note: no argument checking because, pref, enduser unlikely and handled by RT/FW--注意：没有参数检查，因为pref，enduser不太可能，由RT / FW处理
            object setting;
            if (_settings.TryGetValue(key, out setting))
                setSetting((T)setting);
            else if (onFail != null)
                onFail();
        }

        /// <summary>
        ///     Starts a new PetaPoco build configuration.--开始一个新的PetaPoco构建配置。
        /// </summary>
        /// <returns>An instance of <see cref="IDatabaseBuildConfiguration" /> to form a fluent interface.--一个<see cref =“IDatabaseBuildConfiguration”/>的实例来形成一个流畅的界面。</returns>
        public static IDatabaseBuildConfiguration Build()
        {
            return new DatabaseConfiguration();
        }
    }


    /// <summary>
    ///     A static helper class where extensions for <see cref="IDatabaseBuildConfiguration" /> are placed.--一个静态助手类，其中放置了<see cref =“IDatabaseBuildConfiguration”/>的扩展名。
    /// </summary>
    public static class DatabaseConfigurationExtensions
    {
        internal const string CommandTimeout = "CommandTimeout";

        internal const string EnableAutoSelect = "EnableAutoSelect";

        internal const string EnableNamedParams = "EnableNamedParams";

        internal const string Provider = "Provider";

        internal const string ConnectionString = "ConnectionString";

        internal const string ConnectionStringName = "ConnectionStringName";

        internal const string DefaultMapper = "DefaultMapper";

        internal const string IsolationLevel = "IsolationLevel";

        private static void SetSetting(this IDatabaseBuildConfiguration source, string key, object value)
        {
            ((IBuildConfigurationSettings)source).SetSetting(key, value);
        }

        /// <summary>
        ///     Adds a command timeout - see <see cref="IDatabase.CommandTimeout" />.--添加命令超时 - 请参阅<see cref =“IDatabase.CommandTimeout”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="seconds">The timeout in seconds.--超时秒数。</param>
        /// <exception cref="ArgumentException">Thrown when seconds is less than 1.--秒数小于1时抛出。</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingCommandTimeout(this IDatabaseBuildConfiguration source, int seconds)
        {
            if (seconds < 1)
                throw new ArgumentException("Timeout value must be greater than zero.");
            source.SetSetting(CommandTimeout, seconds);
            return source;
        }

        /// <summary>
        ///     Enables named params - see <see cref="IDatabase.EnableNamedParams" />.--启用命名参数 - 参见<see cref =“IDatabase.EnableNamedParams”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration WithNamedParams(this IDatabaseBuildConfiguration source)
        {
            source.SetSetting(EnableNamedParams, true);
            return source;
        }

        /// <summary>
        ///     Disables named params - see <see cref="IDatabase.EnableNamedParams" />.--禁用命名参数 - 请参阅<see cref =“IDatabase.EnableNamedParams”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration WithoutNamedParams(this IDatabaseBuildConfiguration source)
        {
            source.SetSetting(EnableNamedParams, false);
            return source;
        }

        /// <summary>
        ///     Specifies the provider to be used. - see <see cref="IDatabase.Provider" />.--指定要使用的提供程序。 - 见<see cref =“IDatabase.Provider”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="provider">The provider to use.要使用的provider</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="provider" /> is null.--当<paramref name =“provider”/>为空时抛出。</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingProvider<T>(this IDatabaseBuildConfiguration source, T provider)
            where T : class, IProvider
        {
            if (provider == null)
                throw new ArgumentNullException("provider");
            source.SetSetting(Provider, provider);
            return source;
        }

        /// <summary>
        ///     Specifies the provider to be used. - see <see cref="IDatabase.Provider" />.--指定要使用的提供程序。 - 见<see cref =“IDatabase.Provider”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="configure">The configure provider callback.</param>
        /// <param name="provider">The provider to use.要使用的provider</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="provider" /> is null.--当<paramref name =“provider”/>为空时抛出。</exception>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="configure" /> is null.--当<paramref name="configure" />为空时抛出。</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingProvider<T>(this IDatabaseBuildConfiguration source, T provider, Action<T> configure)
            where T : class, IProvider
        {
            if (provider == null)
                throw new ArgumentNullException("provider");
            if (configure == null)
                throw new ArgumentNullException("configure");
            source.SetSetting(Provider, provider);
            return source;
        }

        /// <summary>
        ///     Specifies the provider to be used. - see <see cref="IDatabase.Provider" />.--指定要使用的提供程序。 - 见<see cref =“IDatabase.Provider”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <typeparam name="T">The provider type.</typeparam>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingProvider<T>(this IDatabaseBuildConfiguration source)
            where T : class, IProvider, new()
        {
            source.SetSetting(Provider, new T());
            return source;
        }

        /// <summary>
        ///     Specifies the provider to be used. - see <see cref="IDatabase.Provider" />.--指定要使用的提供程序。 - 见<see cref =“IDatabase.Provider”/>。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="configure">The configure provider callback.</param>
        /// <typeparam name="T">The provider type.</typeparam>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="configure" /> is null.</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingProvider<T>(this IDatabaseBuildConfiguration source, Action<T> configure)
            where T : class, IProvider, new()
        {
            if (configure == null)
                throw new ArgumentNullException("configure");
            var provider = new T();
            configure(provider);
            source.SetSetting(Provider, provider);
            return source;
        }

        /// <summary>
        ///     Enables auto select - see <see cref="IDatabase.EnableAutoSelect" />.
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration WithAutoSelect(this IDatabaseBuildConfiguration source)
        {
            source.SetSetting("EnableAutoSelect", true);
            return source;
        }

        /// <summary>
        ///     Disables auto select - see <see cref="IDatabase.EnableAutoSelect" />.
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration WithoutAutoSelect(this IDatabaseBuildConfiguration source)
        {
            source.SetSetting("EnableAutoSelect", false);
            return source;
        }

        /// <summary>
        ///     Adds a connection string - see <see cref="IDatabase.ConnectionString" />.
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="connectionString">The connection string.--连接字符串。</param>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connectionString" /> is null or empty.</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingConnectionString(this IDatabaseBuildConfiguration source, string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentException("Argument is null or empty", "connectionString");
            source.SetSetting(ConnectionString, connectionString);
            return source;
        }

        /// <summary>
        ///     Adds a connection string name.
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="connectionStringName">The connection string name.</param>
        /// <exception cref="ArgumentException">Thrown when <paramref name="connectionStringName" /> is null or empty.</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingConnectionStringName(this IDatabaseBuildConfiguration source, string connectionStringName)
        {
            if (string.IsNullOrEmpty(connectionStringName))
                throw new ArgumentException("Argument is null or empty", "connectionStringName");
            source.SetSetting(ConnectionStringName, connectionStringName);
            return source;
        }

        /// <summary>
        ///     Specifies The default mapper to use when no specific mapper has been registered.指定当没有注册特定映射器时使用的默认映射器。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="mapper">The mapper to use as the default.--映射器用作默认值。</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="mapper" /> is null.</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingDefaultMapper<T>(this IDatabaseBuildConfiguration source, T mapper)
            where T : class, IMapper
        {
            if (mapper == null)
                throw new ArgumentNullException("mapper");
            source.SetSetting(DefaultMapper, mapper);
            return source;
        }

        /// <summary>
        ///     Specifies The default mapper to use when no specific mapper has been registered.指定当没有注册特定映射器时使用的默认映射器。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="mapper">The mapper to use as the default.--映射器用作默认值。</param>
        /// <param name="configure">The configure mapper callback.</param>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="mapper" /> is null.</exception>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="configure" /> is null.</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingDefaultMapper<T>(this IDatabaseBuildConfiguration source, T mapper, Action<T> configure)
            where T : class, IMapper
        {
            if (mapper == null)
                throw new ArgumentNullException("mapper");
            if (configure == null)
                throw new ArgumentNullException("configure");
            configure(mapper);
            source.SetSetting(DefaultMapper, mapper);
            return source;
        }

        /// <summary>
        ///     Specifies The default mapper to use when no specific mapper has been registered.指定当没有注册特定映射器时使用的默认映射器。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <typeparam name="T">The mapper type.</typeparam>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingDefaultMapper<T>(this IDatabaseBuildConfiguration source)
            where T : class, IMapper, new()
        {
            source.SetSetting(DefaultMapper, new T());
            return source;
        }

        /// <summary>
        ///     Specifies The default mapper to use when no specific mapper has been registered.指定当没有注册特定映射器时使用的默认映射器。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="configure">The configure mapper callback.</param>
        /// <typeparam name="T">The mapper type.</typeparam>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="configure" /> is null.</exception>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingDefaultMapper<T>(this IDatabaseBuildConfiguration source, Action<T> configure)
            where T : class, IMapper, new()
        {
            if (configure == null)
                throw new ArgumentNullException("configure");

            var mapper = new T();
            configure(mapper);
            source.SetSetting(DefaultMapper, mapper);
            return source;
        }

        /// <summary>
        ///     Specifies the transaction isolation level to use.--指定要使用的事务隔离级别。
        /// </summary>
        /// <param name="source">The configuration source.--配置源。</param>
        /// <param name="isolationLevel"></param>
        /// <returns>The configuration source to form a fluent interface.--配置源形成流畅的界面。</returns>
        public static IDatabaseBuildConfiguration UsingIsolationLevel(this IDatabaseBuildConfiguration source, IsolationLevel isolationLevel)
        {
            source.SetSetting(IsolationLevel, isolationLevel);
            return source;
        }

        /// <summary>
        ///     Creates an instance of PetaPooc using the specified <paramref name="source" />.--使用指定的<paramref name =“source”/>创建PetaPooc实例。
        /// </summary>
        /// <param name="source">The configuration source used to create and configure an instance of PetaPoco.--用于创建和配置PetaPoco实例的配置源</param>
        /// <returns>An instance of PetaPoco.--PetaPoco的一个实例。</returns>
        public static IDatabase Create(this IDatabaseBuildConfiguration source)
        {
            return new Database(source);
        }
    }


    public interface IAlterPoco
    {
        /// <summary>
        ///     Performs an SQL Insert.--执行SQL插入
        /// </summary>
        /// <param name="tableName">The name of the table to insert into.--要插入的表的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be inserted.--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables.--新记录的自动分配主键，或非自动递增表的空值</returns>
        object Insert(string tableName, object poco);

        /// <summary>
        ///     Performs an SQL Insert.--执行SQL插入
        /// </summary>
        /// <param name="tableName">The name of the table to insert into.--要插入的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称.</param>
        /// <param name="poco">The POCO object that specifies the column values to be inserted.--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables.--新记录的自动分配主键，或非自动递增表的空值</returns>
        object Insert(string tableName, string primaryKeyName, object poco);

        /// <summary>
        ///     Performs an SQL Insert.--执行SQL插入
        /// </summary>
        /// <param name="tableName">The name of the table to insert into.--要插入的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称.</param>
        /// <param name="autoIncrement">True if the primary key is automatically allocated by the DB.--如果主键由数据库自动分配，则为真</param>
        /// <param name="poco">The POCO object that specifies the column values to be inserted.--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables.--新记录的自动分配主键，或非自动递增表的空值</returns>
        /// <remarks>
        ///     Inserts a poco into a table. If the poco has a property with the same name
        ///     as the primary key, the id of the new record is assigned to it. Either way,
        ///     the new id is returned.--将poco插入table。 如果poco具有与主键名称相同的属性，则会为其分配新记录的ID。 无论哪种方式，都会返回新的ID。
        /// </remarks>
        object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco);

        /// <summary>
        ///     Performs an SQL Insert.--执行SQL插入
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be inserted.--指定要插入的列值的POCO对象</param>
        /// <returns>The auto allocated primary key of the new record, or null for non-auto-increment tables.--新记录的自动分配主键，或非自动递增表的空值</returns>
        /// <remarks>
        ///     The name of the table, it's primary key and whether it's an auto-allocated primary key are retrieved
        ///     from the POCO's attributes--表的名称，它是主键，是否是自动分配的主键，从POCO的属性中检索
        /// </remarks>
        object Insert(object poco);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <returns>The number of affected records--受影响记录的数量</returns>
        int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(string tableName, string primaryKeyName, object poco);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="tableName">The name of the table to update--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column of the table--表的主键列的名称</param>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(object poco, IEnumerable<string> columns);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(object poco);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(object poco, object primaryKeyValue);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <param name="poco">The POCO object that specifies the column values to be updated--指定要更新的列值的POCO对象</param>
        /// <param name="primaryKeyValue">The primary key of the record to be updated--要更新的记录的主键</param>
        /// <param name="columns">The column names of the columns to be updated, or null for all--要更新的列的列名称，或全部为null</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update(object poco, object primaryKeyValue, IEnumerable<string> columns);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to update--POCO类的属性指定要更新的表的名称</typeparam>
        /// <param name="sql">The SQL update and condition clause (ie: everything after "UPDATE tablename"--SQL更新和条件子句（即：“UPDATE tablename”之后的所有内容）</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update<T>(string sql, params object[] args);

        /// <summary>
        ///     Performs an SQL update--执行SQL更新
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to update--POCO类的属性指定要更新的表的名称</typeparam>
        /// <param name="sql">
        ///     An SQL builder object representing the SQL update and condition clause (ie: everything after "UPDATE
        ///     tablename"--表示SQL更新和条件子句的SQL构建器对象（即：“UPDATE tablename”之后的所有内容）
        /// </param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Update<T>(Sql sql);

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <param name="tableName">The name of the table to delete from--要删除的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">The POCO object whose primary key value will be used to delete the row--主键值将用于删除行的POCO对象</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        int Delete(string tableName, string primaryKeyName, object poco);

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <param name="tableName">The name of the table to delete from--要删除的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">
        ///     The POCO object whose primary key value will be used to delete the row (or null to use the supplied
        ///     primary key value)--主键值将用于删除行的POCO对象（或使用提供的主键值的空值）
        /// </param>
        /// <param name="primaryKeyValue">
        ///     The value of the primary key identifing the record to be deleted (or null, or get this
        ///     value from the POCO instance)--识别要删除的记录的主键的值（或null，或从POCO实例获取该值）
        /// </param>
        /// <returns>The number of rows affected受影响的行数</returns>
        int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue);

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <param name="poco">The POCO object specifying the table name and primary key value of the row to be deleted--指定要删除的行的表名和主键值的POCO对象</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        int Delete(object poco);

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <typeparam name="T">The POCO class whose attributes identify the table and primary key to be used in the delete--POCO类的属性标识要删除的表和主键</typeparam>
        /// <param name="pocoOrPrimaryKey">The value of the primary key of the row to delete--要删除的行的主键的值</param>
        /// <returns></returns>
        int Delete<T>(object pocoOrPrimaryKey);

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to delete from--POCO类的属性指定要从中删除的表的名称</typeparam>
        /// <param name="sql">The SQL condition clause identifying the row to delete (ie: everything after "DELETE FROM tablename"--标识要删除的行的SQL条件子句（即：“DELETE FROM tablename”之后的所有内容）</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Delete<T>(string sql, params object[] args);

        /// <summary>
        ///     Performs an SQL Delete--执行SQL删除
        /// </summary>
        /// <typeparam name="T">The POCO class who's attributes specify the name of the table to delete from--POCO类的属性指定要从中删除的表的名称</typeparam>
        /// <param name="sql">
        ///     An SQL builder object representing the SQL condition clause identifying the row to delete (ie:
        ///     everything after "UPDATE tablename"--表示SQL条件子句的SQL构建器对象，用于标识要删除的行（即：“UPDATE tablename”之后的所有内容）
        /// </param>
        /// <returns>The number of affected rows--受影响的行数</returns>
        int Delete<T>(Sql sql);

        /// <summary>
        ///     Check if a poco represents a new row--检查poco是否代表新行
        /// </summary>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">The object instance whose "newness" is to be tested--要测试其“新行”的对象实例</param>
        /// <returns>True if the POCO represents a record already in the database--如果POCO表示已经在数据库中的记录，则为真</returns>
        /// <remarks>This method simply tests if the POCO's primary key column property has been set to something non-zero.--该方法只是测试POCO的主键列属性是否被设置为非零。</remarks>
        bool IsNew(string primaryKeyName, object poco);

        /// <summary>
        ///     Check if a poco represents a new row--检查poco是否代表新行
        /// </summary>
        /// <param name="poco">The object instance whose "newness" is to be tested--要测试其“新行”的对象实例</param>
        /// <returns>True if the POCO represents a record already in the database--如果POCO表示已经在数据库中的记录，则为真</returns>
        /// <remarks>This method simply tests if the POCO's primary key column property has been set to something non-zero.--该方法只是测试POCO的主键列属性是否被设置为非零。</remarks>
        bool IsNew(object poco);

        /// <summary>
        ///     Saves a POCO by either performing either an SQL Insert or SQL Update--通过执行SQL插入或SQL更新来保存POCO
        /// </summary>
        /// <param name="tableName">The name of the table to be updated--要更新的表的名称</param>
        /// <param name="primaryKeyName">The name of the primary key column--主键列的名称</param>
        /// <param name="poco">The POCO object to be saved--要保存的POCO对象</param>
        void Save(string tableName, string primaryKeyName, object poco);

        /// <summary>
        ///     Saves a POCO by either performing either an SQL Insert or SQL Update--通过执行SQL插入或SQL更新来保存POCO
        /// </summary>
        /// <param name="poco">The POCO object to be saved--要保存的POCO对象</param>
        void Save(object poco);
    }


    /// <summary>
    ///     Represents the build configuration settings contract.--表示构建配置设置接口。
    /// </summary>
    public interface IBuildConfigurationSettings
    {
        /// <summary>
        ///     Sets the setting against the specified key.--根据指定的键设置设置。
        /// </summary>
        /// <param name="key">The setting's key.--设置的键。</param>
        /// <param name="value">The setting's value.--设定值。</param>
        void SetSetting(string key, object value);

        /// <summary>
        ///     Tries to get the setting and calls the <paramref name="setSetting" /> to set the value if found.--尝试获取设置并调用<paramref name =“setSetting”/>来设置值。
        /// </summary>
        /// <typeparam name="T">The setting type.--设置类型。</typeparam>
        /// <param name="key">The setting's key.--设置的键。</param>
        /// <param name="setSetting">The set setting callback.--设置设置回调。</param>
        /// <param name="onFail">The on fail callback, called when no setting can be set.--在无效回调时，无法设置设置时调用。</param>
        void TryGetSetting<T>(string key, Action<T> setSetting, Action onFail = null);
    }


    /// <summary>
    ///     Specifies the database contract.--指定数据库接口
    /// </summary>
    public interface IDatabase : IDisposable, IQuery, IAlterPoco, IExecute, ITransactionAccessor
    {
        /// <summary>
        ///     Gets the default mapper. (Default is <see cref="ConventionMapper" />)--获取默认的映射器。 （默认值为<see cref =“ConventionMapper”/>）
        /// </summary>
        /// <returns>
        ///     The default mapper.--默认的映射器
        /// </returns>
        IMapper DefaultMapper { get; }

        /// <summary>
        ///     Gets the SQL of the last executed statement--获取最后执行的语句的SQL
        /// </summary>
        /// <returns>
        ///     The last executed SQL.--最后执行的SQL。
        /// </returns>
        string LastSQL { get; }

        /// <summary>
        ///     Gets the arguments to the last execute statement--获取最后一个execute语句的参数
        /// </summary>
        /// <returns>
        ///     The last executed SQL arguments.--最后执行的SQL参数。
        /// </returns>
        object[] LastArgs { get; }

        /// <summary>
        ///     Gets a formatted string describing the last executed SQL statement and it's argument values--获取描述最后执行的SQL语句的格式化字符串，它是参数值
        /// </summary>
        /// <returns>
        ///     The formatted string.--格式化的字符串。
        /// </returns>
        string LastCommand { get; }

        /// <summary>
        ///     Gets or sets the enable auto select. (Default is True)--获取或设置启用自动选择。 （默认值为True）
        /// </summary>
        /// <remarks>
        ///     When set to true, PetaPoco will automatically create the "SELECT columns" section of the query for any query which
        ///     is found to require them.--当设置为true时，PetaPoco会自动创建查询的“SELECT columns”部分，以查找发现需要的查询。
        /// </remarks>
        /// <returns>
        ///     True, if auto select is enabled; Else, false.--是的，如果启用自动选择; 否则，假。
        /// </returns>
        bool EnableAutoSelect { get; set; }

        /// <summary>
        ///     Gets the flag for whether named params are enabled. (Default is True)--获取是否启用命名参数的标志。 （默认值为True）
        /// </summary>
        /// <remarks>
        ///     When set to true, parameters can be named ?myparam and populated from properties of the passed in argument values.--当设置为true时，参数可以命名为myparam并从传入的参数值的属性中填充。
        /// </remarks>
        /// <returns>
        ///     True, if named parameters are enabled; Else, false.--真的，如果命名参数被启用; 否则，假。
        /// </returns>
        bool EnableNamedParams { get; set; }

        /// <summary>
        ///     Sets the timeout value, in seconds, which PetaPoco applies to all <see cref="IDbCommand.CommandTimeout" />.
        ///     (Default is 0)--设置PetaPoco适用于所有<see cref =“IDbCommand.CommandTimeout”/>的超时值（以秒为单位）。 （默认值为0）
        /// </summary>
        /// <remarks>
        ///     If the current value is zero PetaPoco will not set the command timeout, and therefor, the .net default (30 seconds)
        ///     will be in affect.--如果当前值为零，PetaPoco将不会设置命令超时，因此.net默认（30秒）将会生效。
        /// </remarks>
        /// <returns>
        ///     The current command timeout.--当前命令超时时间。
        /// </returns>
        int CommandTimeout { get; set; }

        /// <summary>
        ///     Sets the timeout value for the next (and only next) SQL statement.--设置下一个（而且只有下一个）SQL语句的超时值。
        /// </summary>
        /// <remarks>
        ///     This is a one-time settings, which after use, will return the <see cref="CommandTimeout" /> setting.--这是一次性设置，使用后将返回<see cref =“CommandTimeout”/>设置。
        /// </remarks>
        /// <returns>
        ///     The one time command timeout.--一次命令超时时间。
        /// </returns>
        int OneTimeCommandTimeout { get; set; }

        /// <summary>
        ///     Gets the current <seealso cref="Provider" />.--获取当前的<seealso cref =“Provider”/>。
        /// </summary>
        /// <returns>
        ///     The current database provider.--当前的database provider
        /// </returns>
        IProvider Provider { get; }

        /// <summary>
        ///     Gets the connection string..--获取连接字符串。
        /// </summary>
        /// <returns>
        ///     The connection string.--连接字符串。
        /// </returns>
        string ConnectionString { get; }

        /// <summary>
        ///     Gets or sets the transaction isolation level.--获取或设置事务隔离级别。
        /// </summary>
        /// <remarks>
        ///     When value is null, the underlying providers default isolation level is used.--当值为null时，使用底层提供程序默认隔离级别。
        /// </remarks>
        IsolationLevel? IsolationLevel { get; set; }

        /// <summary>
        ///     Starts or continues a transaction.开始或继续事务。
        /// </summary>
        /// <returns>An ITransaction reference that must be Completed or disposed必须完成或处置的ITransaction参考</returns>
        /// <remarks>
        ///     This method makes management of calls to Begin/End/CompleteTransaction easier.这种方法使得对Begin / End / CompleteTransaction的调用更容易管理。
        ///     The usage pattern for this should be://这个使用模式应该是：
        ///     using (var tx = db.GetTransaction())
        ///     {
        ///     // Do stuff
        ///     db.Update(...);
        ///     // Mark the transaction as complete
        ///     tx.Complete();
        ///     }
        ///     Transactions can be nested but they must all be completed otherwise the entire事务可以嵌套，但是它们必须全部被完成
        ///     transaction is aborted.事务中止。
        /// </remarks>
        ITransaction GetTransaction();

        /// <summary>
        ///     Starts a transaction scope, see GetTransaction() for recommended usage启动事务，请参阅GetTransaction（）以获取推荐用法
        /// </summary>
        void BeginTransaction();

        /// <summary>
        ///     Aborts the entire outer most transaction scope中止整个外部的最大事务范围
        /// </summary>
        /// <remarks>
        ///     Called automatically by Transaction.Dispose() if the transaction wasn't completed.
        ///     如果事务未完成，则由Transaction.Dispose（）自动调用。
        /// </remarks>
        void AbortTransaction();

        /// <summary>
        ///     Marks the current transaction scope as complete.将当前事务范围标记为完整。
        /// </summary>
        void CompleteTransaction();
    }


    /// <summary>
    ///     A helper interface which enables fluent configuration extension methods.--一个辅助界面，使流畅的配置扩展方法。
    /// </summary>
    public interface IDatabaseBuildConfiguration
    {

    }


    public interface IExecute
    {
        /// <summary>
        ///     Executes a non-query command执行非查询命令（增删改操作）
        /// </summary>
        /// <param name="sql">The SQL statement to execute要执行的SQL语句</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        int Execute(string sql, params object[] args);

        /// <summary>
        ///     Executes a non-query command执行非查询命令（增删改操作）
        /// </summary>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The number of rows affected受影响的行数</returns>
        int Execute(Sql sql);

        /// <summary>
        ///     Executes a query and return the first column of the first row in the result set.//执行查询并返回结果集中第一行的第一列。
        /// </summary>
        /// <typeparam name="T">The type that the result value should be cast to//要转换结果值的类型</typeparam>
        /// <param name="sql">The SQL query to execute//要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>The scalar value cast to T //返回泛型T类型</returns>
        T ExecuteScalar<T>(string sql, params object[] args);

        /// <summary>
        ///      Executes a query and return the first column of the first row in the result set.//执行查询并返回结果集中第一行的第一列。
        /// </summary>
        /// <typeparam name="T">The type that the result value should be cast to//要转换结果值的类型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The scalar value cast to T //返回泛型T类型</returns>
        T ExecuteScalar<T>(Sql sql);
    }


    /// <summary>
    ///     An interface used to hide the 4 System.Object instance methods from the API in Visual Studio intellisense.--用于从Visual Studio Intellisense中的API隐藏4个System.Object实例方法的接口。
    /// </summary>
    /// <remarks>
    ///     Reference Project: MircoLite ORM (https://github.com/TrevorPilley/MicroLite)--参考项目：MircoLite ORM（https://github.com/TrevorPilley/MicroLite）
    ///     Author: Trevor Pilley--作者：Trevor Pilley
    ///     Source: https://github.com/TrevorPilley/MicroLite/blob/develop/MicroLite/IHideObjectMethods.cs,//--资料来源
    /// </remarks>
    [EditorBrowsable(EditorBrowsableState.Never)]
    public interface IHideObjectMethods
    {
        /// <summary>
        ///     Determines whether the specified <see cref="System.Object" /> is equal to this instance.--确定指定的<see cref =“System.Object”/>是否等于此实例。
        /// </summary>
        /// <param name="other">The <see cref="System.Object" /> to compare with this instance.--<see cref =“System.Object”/>与此实例进行比较。</param>
        /// <returns>
        ///     <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.
        ///     如果指定的<see cref =“System.Object”/>等于此实例返回true; 除此以外，返回false
        /// </returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        bool Equals(object other);

        /// <summary>
        ///     Returns a hash code for this instance.--返回此实例的哈希码。
        /// </summary>
        /// <returns>
        ///     A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.--用于此实例的哈希码，适用于散列算法和数据结构（如散列表）。
        /// </returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        int GetHashCode();

        /// <summary>
        ///     Gets the type.--获取类型。
        /// </summary>
        /// <returns>The type of the object.--对象的类型。</returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
            Justification = "The method is defined on System.Object, this interface is just to hide it from intelisense in Visual Studio")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "GetType",
            Justification = "The method is defined on System.Object, this interface is just to hide it from intelisense in Visual Studio")]
        Type GetType();

        /// <summary>
        ///     Returns a <see cref="System.String" /> that represents this instance.--返回代表此实例的<see cref =“System.String”/>。
        /// </summary>
        /// <returns>
        ///     A <see cref="System.String" /> that represents this instance.--表示此实例的<see cref =“System.String”/>。
        /// </returns>
        [EditorBrowsable(EditorBrowsableState.Never)]
        string ToString();
    }


    public interface IQuery
    {
        /// <summary>
        ///     Runs an SQL query, returning the results as an IEnumerable collection--运行SQL查询，将结果作为IEnumerable集合返回
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>An enumerable collection of result records--一个可枚举的结果记录集合</returns>
        /// <remarks>
        ///     For some DB providers, care should be taken to not start a new Query before finishing with
        ///     and disposing the previous one. In cases where this is an issue, consider using Fetch which
        ///     returns the results as a List rather than an IEnumerable.
        ///     对于某些DB提供商，在完成和处理前一个DB提供程序之前，应小心不要启动新的查询。 在这是一个问题的情况下，请考虑使用Fetch返回结果作为列表而不是IEnumerable。
        /// </remarks>
        IEnumerable<T> Query<T>(string sql, params object[] args);

        /// <summary>
        ///     Runs an SQL query, returning the results as an IEnumerable collection--运行SQL查询，将结果作为IEnumerable集合返回
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>An enumerable collection of result records--一个可枚举的结果记录集合</returns>
        /// <remarks>
        ///     For some DB providers, care should be taken to not start a new Query before finishing with
        ///     and disposing the previous one. In cases where this is an issue, consider using Fetch which
        ///     returns the results as a List rather than an IEnumerable.
        ///     对于某些DB提供商，在完成和处理前一个DB提供程序之前，应小心不要启动新的查询。 在这是一个问题的情况下，请考虑使用Fetch返回结果作为列表而不是IEnumerable。
        /// </remarks>
        IEnumerable<T> Query<T>(Sql sql);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Query<T1, T2>(string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Query<T1, T2, T3>(string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Query<T1, T2, T3, T4>(string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Query<T1, T2>(Sql sql);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Query<T1, T2, T3>(Sql sql);

        /// <summary>
        ///     Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Query<T1, T2, T3, T4>(Sql sql);

        /// <summary>
        ///     Performs a multi-poco query
        /// </summary>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="types">An array of Types representing the POCO types of the returned result set.--一组表示返回结果集的POCO类型的类型。</param>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Query<TRet>(Type[] types, object cb, string sql, params object[] args);

        /// <summary>
        ///     Runs a query and returns the result set as a typed list运行查询并将结果集返回为类型列表
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query to execute//要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>>A List holding the results of the query//包含查询结果的列表</returns>
        List<T> Fetch<T>(string sql, params object[] args);

        /// <summary>
        ///     Runs a query and returns the result set as a typed list运行查询并将结果集返回为类型列表
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>>A List holding the results of the query//包含查询结果的列表</returns>
        List<T> Fetch<T>(Sql sql);

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sqlCount">The SQL to retrieve the total number of records--SQL检索总记录数</param>
        /// <param name="countArgs">Arguments to any embedded parameters in the sqlCount statement--sqlCount语句中任何嵌入参数的参数</param>
        /// <param name="sqlPage">The SQL To retrieve a single page of results--SQL检索单个页面的结果</param>
        /// <param name="pageArgs">Arguments to any embedded parameters in the sqlPage statement--sqlPage语句中任何嵌入参数的参数</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     This method allows separate SQL statements to be explicitly provided for the two parts of the page query.
        ///     The page and itemsPerPage parameters are not used directly and are used simply to populate the returned Page
        ///     object.--此方法允许为页面查询的两个部分显式提供单独的SQL语句。 页面和itemsPerPage参数不直接使用，仅用于填充返回的页面对象。
        /// </remarks>
        Page<T> Page<T>(long page, long itemsPerPage, string sqlCount, object[] countArgs, string sqlPage, object[] pageArgs);

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">The base SQL query--基本SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.  It will also execute a second query to retrieve the
        ///     total number of records in the result set.--PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。 它还将执行第二个查询来检索结果集中的记录总数。
        /// </remarks>
        Page<T> Page<T>(long page, long itemsPerPage, string sql, params object[] args);

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.  It will also execute a second query to retrieve the
        ///     total number of records in the result set.--PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。 它还将执行第二个查询来检索结果集中的记录总数。
        /// </remarks>
        Page<T> Page<T>(long page, long itemsPerPage, Sql sql);

        /// <summary>
        ///     Retrieves a page of records	and the total number of available records--检索一页记录和可用记录的总数
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sqlCount">An SQL builder object representing the SQL to retrieve the total number of records--表示SQL的SQL构建器对象来检索总记录数</param>
        /// <param name="sqlPage">An SQL builder object representing the SQL to retrieve a single page of results--表示SQL的SQL构建器对象来检索单个页面的结果</param>
        /// <returns>A Page of results--一页结果</returns>
        /// <remarks>
        ///     This method allows separate SQL statements to be explicitly provided for the two parts of the page query.
        ///     The page and itemsPerPage parameters are not used directly and are used simply to populate the returned Page
        ///     object.--此方法允许为页面查询的两个部分显式提供单独的SQL语句。 页面和itemsPerPage参数不直接使用，仅用于填充返回的页面对象。
        /// </remarks>
        Page<T> Page<T>(long page, long itemsPerPage, Sql sqlCount, Sql sqlPage);

        /// <summary>
        ///     Retrieves a page of records (without the total count)--检索一页记录（不计总数）
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">The base SQL query--基本SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.----PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。
        /// </remarks>
        List<T> Fetch<T>(long page, long itemsPerPage, string sql, params object[] args);

        /// <summary>
        ///     Retrieves a page of records (without the total count)--检索一页记录（不计总数）
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="page">The 1 based page number to retrieve--要检索的基于1的页码</param>
        /// <param name="itemsPerPage">The number of records per page--每页记录的数量</param>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified page.--PetaPoco将自动修改提供的SELECT语句，仅检索指定页面的记录。
        /// </remarks>
        List<T> Fetch<T>(long page, long itemsPerPage, Sql sql);

        /// <summary>
        ///     Retrieves a range of records from result set--从结果集中检索一系列记录
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="skip">The number of rows at the start of the result set to skip over--结果集开始处的行数跳过</param>
        /// <param name="take">The number of rows to retrieve--要检索的行数</param>
        /// <param name="sql">The base SQL query--基本SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified range.--PetaPoco将自动修改提供的SELECT语句，仅检索指定范围的记录。
        /// </remarks>
        List<T> SkipTake<T>(long skip, long take, string sql, params object[] args);

        /// <summary>
        ///     Retrieves a range of records from result set--从结果集中检索一系列记录
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="skip">The number of rows at the start of the result set to skip over--结果集开始处的行数跳过</param>
        /// <param name="take">The number of rows to retrieve--要检索的行数</param>
        /// <param name="sql">An SQL builder object representing the base SQL query and it's arguments--表示基本SQL查询的SQL构建器对象及其参数</param>
        /// <returns>A List of results--结果列表</returns>
        /// <remarks>
        ///     PetaPoco will automatically modify the supplied SELECT statement to only retrieve the
        ///     records for the specified range.--PetaPoco将自动修改提供的SELECT语句，仅检索指定范围的记录。
        /// </remarks>
        List<T> SkipTake<T>(long skip, long take, Sql sql);

        /// <summary>
        ///     Checks for the existence of a row with the specified primary key value.--检查是否存在具有指定主键值的行。
        /// </summary>
        /// <typeparam name="T">The Type representing the table being queried--表示要查询的表的类型</typeparam>
        /// <param name="primaryKey">The primary key value to look for--要查找的主键值</param>
        /// <returns>True if a record with the specified primary key value exists.--如果存在具有指定主键值的记录，则为真。</returns>
        bool Exists<T>(object primaryKey);

        /// <summary>
        ///     Checks for the existence of a row matching the specified condition--检查是否存在符合指定条件的行
        /// </summary>
        /// <typeparam name="T">The Type representing the table being queried--表示要查询的表的类型</typeparam>
        /// <param name="sqlCondition">The SQL expression to be tested for (ie: the WHERE expression)--要测试的SQL表达式（即：WHERE表达式）</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>True if a record matching the condition is found.--如果找到符合条件的记录，则返回true。</returns>
        bool Exists<T>(string sqlCondition, params object[] args);

        /// <summary>
        ///     Returns the record with the specified primary key value--返回具有指定主键值的记录
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="primaryKey">The primary key value of the record to fetch--要获取的记录的主键值</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     Throws an exception if there are zero or more than one record with the specified primary key value.--如果具有指定主键值的零个或多个记录，则抛出异常。
        /// </remarks>
        T Single<T>(object primaryKey);

        /// <summary>
        ///     Runs a query that should always return a single row.--运行一个应该总是返回一行的查询。
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     Throws an exception if there are zero or more than one matching record--如果存在零个或多个匹配记录，则抛出异常
        /// </remarks>
        T Single<T>(string sql, params object[] args);

        /// <summary>
        ///     Runs a query that should always return a single row.--运行一个应该总是返回一行的查询。
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     Throws an exception if there are zero or more than one matching record--如果存在零个或多个匹配记录，则抛出异常
        /// </remarks>
        T Single<T>(Sql sql);

        /// <summary>
        ///     Runs a query that should always return either a single row, or no rows--运行一个应该总是返回单行或没有行的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The single record matching the specified primary key value, or default(T) if no matching rows--单个记录匹配指定的主键值，如果没有匹配的行，则为默认（T）</returns>
        T SingleOrDefault<T>(Sql sql);

        /// <summary>
        ///     Returns the record with the specified primary key value, or the default value if not found--返回具有指定主键值的记录，如果未找到则返回默认值
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="primaryKey">The primary key value of the record to fetch--要获取的记录的主键值</param>
        /// <returns>The single record matching the specified primary key value--单个记录匹配指定的主键值</returns>
        /// <remarks>
        ///     If there are no records with the specified primary key value, default(T) (typically null) is returned.--如果没有指定主键值的记录，则返回default（T）（通常为null）。
        /// </remarks>
        T SingleOrDefault<T>(object primaryKey);

        /// <summary>
        ///     Runs a query that should always return either a single row, or no rows--运行一个应该总是返回单行或没有行的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The single record matching the specified primary key value, or default(T) if no matching rows--单个记录匹配指定的主键值，如果没有匹配的行，则为默认（T）</returns>
        T SingleOrDefault<T>(string sql, params object[] args);

        /// <summary>
        ///     Runs a query that should always return at least one return--运行一个应该总是返回至少一个返回的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The first record in the result set--结果集中的第一个记录</returns>
        T First<T>(string sql, params object[] args);

        /// <summary>
        ///     Runs a query that should always return at least one return--运行一个应该总是返回至少一个返回的查询
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The first record in the result set--结果集中的第一个记录</returns>
        T First<T>(Sql sql);

        /// <summary>
        ///     Runs a query and returns the first record, or the default value if no matching records--运行查询并返回第一条记录，如果没有匹配的记录，则返回默认值
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">The SQL query--要查询的SQL</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL statement对SQL语句中任何嵌入参数的参数</param>
        /// <returns>The first record in the result set, or default(T) if no matching rows--结果集中的第一条记录，如果没有匹配行，则为默认（T）</returns>
        T FirstOrDefault<T>(string sql, params object[] args);

        /// <summary>
        ///     Runs a query and returns the first record, or the default value if no matching records--运行查询并返回第一条记录，如果没有匹配的记录，则返回默认值
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>The first record in the result set, or default(T) if no matching rows--结果集中的第一条记录，如果没有匹配行，则为默认（T）</returns>
        T FirstOrDefault<T>(Sql sql);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<TRet> Fetch<T1, T2, TRet>(Func<T1, T2, TRet> cb, Sql sql);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<TRet> Fetch<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb, Sql sql);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <typeparam name="TRet">The returned list POCO type--返回的列表POCO类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<TRet> Fetch<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb, Sql sql);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<T1> Fetch<T1, T2>(string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<T1> Fetch<T1, T2, T3>(string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<T1> Fetch<T1, T2, T3, T4>(string sql, params object[] args);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<T1> Fetch<T1, T2>(Sql sql);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<T1> Fetch<T1, T2, T3>(Sql sql);

        /// <summary>
        ///     Perform a multi-poco fetch--执行多poco提取
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The fourth POCO type--第四种POCO型</typeparam>
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param>
        /// <returns>A collection of POCO's as a List--POCO作为列表的集合</returns>
        List<T1> Fetch<T1, T2, T3, T4>(Sql sql);

        /// <summary> 
        /// Perform a multi-results set query--执行多结果集查询 --执行多结果集查询
        /// </summary> 
        /// <param name="sql">An SQL builder object representing the query and it's arguments//表示查询的SQL构建器对象及其参数</param> 
        /// <returns>A GridReader to be queried--要查询的GridReader</returns> 
        IGridReader QueryMultiple(Sql sql);


        /// <summary> 
        /// Perform a multi-results set query--执行多结果集查询 --执行多结果集查询
        /// </summary> 
        /// <param name="sql">The SQL query to be executed--要执行的SQL查询</param> 
        /// <param name="args">Arguments to any embedded parameters in the SQL//SQL中任何嵌入参数的参数</param> 
        /// <returns>A GridReader to be queried--要查询的GridReader</returns>
        IGridReader QueryMultiple(string sql, params object[] args);
    }


    /// <summary>
    ///     Represents a contract which exposes the current <see cref="IDbTransaction" /> instance.--表示公开当前<see cref =“IDbTransaction”/>实例的接口。
    /// </summary>
    public interface ITransactionAccessor
    {
        /// <summary>
        ///     Gets the current transaction instance.获取当前事务实例。
        /// </summary>
        /// <returns>
        ///     The current transaction instance; else, <c>null</c> if not transaction is in progress.--当前的事务实例; 否则，如果不是正在进行事务，则<c> null </ c>。
        /// </returns>
        IDbTransaction Transaction { get; }
    }


    /* 
    Thanks to Adam Schroder (@schotime) for this.
	
    This extra file provides an implementation of DbProviderFactory for early versions of the Oracle
    drivers that don't include include it.  For later versions of Oracle, the standard OracleProviderFactory
    class should work fine
	
    Uses reflection to load Oracle.DataAccess assembly and in-turn create connections and commands
	
    Currently untested.
	
    Usage:   
		
            new PetaPoco.Database("<connstring>", new PetaPoco.OracleProvider())
	
    Or in your app/web config (be sure to change ASSEMBLYNAME to the name of your 
    assembly containing OracleProvider.cs)
	
        <connectionStrings>
            <add
                name="oracle"
                connectionString="WHATEVER"
                providerName="Oracle"
                />
        </connectionStrings>

        <system.data>
            <DbProviderFactories>
                <add name="PetaPoco Oracle Provider" invariant="Oracle" description="PetaPoco Oracle Provider" 
                                type="PetaPoco.OracleProvider, ASSEMBLYNAME" />
            </DbProviderFactories>
        </system.data>

     */

    public class OracleProvider : DbProviderFactory
    {
        private const string _assemblyName = "Oracle.DataAccess";
        private const string _connectionTypeName = "Oracle.DataAccess.Client.OracleConnection";
        private const string _commandTypeName = "Oracle.DataAccess.Client.OracleCommand";
        private static Type _connectionType;
        private static Type _commandType;

        // Required for DbProviderFactories.GetFactory() to work.--需要DbProviderFactories.GetFactory（）才能正常工作。
        public static OracleProvider Instance = new OracleProvider();

        public OracleProvider()
        {
            _connectionType = TypeFromAssembly(_connectionTypeName, _assemblyName);
            _commandType = TypeFromAssembly(_commandTypeName, _assemblyName);
            if (_connectionType == null)
                throw new InvalidOperationException("Can't find Connection type: " + _connectionTypeName);
        }

        public override DbConnection CreateConnection()
        {
            return (DbConnection)Activator.CreateInstance(_connectionType);
        }

        public override DbCommand CreateCommand()
        {
            DbCommand command = (DbCommand)Activator.CreateInstance(_commandType);

            var oracleCommandBindByName = _commandType.GetProperty("BindByName");
            oracleCommandBindByName.SetValue(command, true, null);

            return command;
        }

        public static Type TypeFromAssembly(string typeName, string assemblyName)
        {
            try
            {
                // Try to get the type from an already loaded assembly--尝试从已加载的程序集获取类型
                Type type = Type.GetType(typeName);

                if (type != null)
                {
                    return type;
                }

                if (assemblyName == null)
                {
                    // No assembly was specified for the type, so just fail--没有为类型指定程序集，所以只是失败
                    string message = "Could not load type " + typeName + ". Possible cause: no assembly name specified.";
                    throw new TypeLoadException(message);
                }

                Assembly assembly = Assembly.Load(assemblyName);

                if (assembly == null)
                {
                    throw new InvalidOperationException("Can't find assembly: " + assemblyName);
                }

                type = assembly.GetType(typeName);

                if (type == null)
                {
                    return null;
                }

                return type;
            }
            catch (Exception)
            {
                return null;
            }
        }
    }


    /// <summary>
    ///     Represents an attribute which can decorate a Poco property to mark the property as a column. It may also optionally
    ///     supply the DB column name.--表示可以装饰Poco属性以将属性标记为列的属性。 它也可以选择提供DB列名称。
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class ColumnAttribute : Attribute
    {
        /// <summary>
        ///     The SQL name of the column--列的SQL名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        ///     True if time and date values returned through this column should be forced to UTC DateTimeKind. (no conversion is
        ///     applied - the Kind of the DateTime property
        ///     is simply set to DateTimeKind.Utc instead of DateTimeKind.Unknown.
        ///      如果通过此列返回的时间和日期值应强制为UTC DateTimeKind，则为真。 （不应用转换 - DateTime属性的种类简单地设置为DateTimeKind.Utc而不是DateTimeKind.Unknown。
        /// </summary>
        public bool ForceToUtc { get; set; }

        /// <summary>
        ///     The insert template. If not null, this template is used for generating the insert section instead of the deafult
        ///     string.Format("{0}{1}", paramPrefix, index"). Setting this allows DB related interactions, such as "CAST({0}{1} AS json)"
        ///     插入模板。 如果不为null，则该模板用于生成插入部分而不是deafult string.Format（“{0} {1}”，paramPrefix，index“）。设置此允许DB相关的交互，如”CAST（{0 } {1} AS json）“
        /// </summary>
        public string InsertTemplate { get; set; }

        /// <summary>
        ///     The update template. If not null, this template is used for generating the update section instead of the deafult
        ///     string.Format("{0} = {1}{2}", colName, paramPrefix, index"). Setting this allows DB related interactions, such as "{0} = CAST({1}{2} AS json)"
        ///     更新模板。 如果不为空，则该模板用于生成更新部分而不是deafult string.Format（“{0} = {1} {2}”，colName，paramPrefix，index“）。 作为“{0} = CAST（{1} {2} AS json）”
        /// </summary>
        public string UpdateTemplate { get; set; }

        /// <summary>
        ///     Constructs a new instance of the <seealso cref="ColumnAttribute" />.--构造一个新的<seealso cref =“ColumnAttribute”/>实例。
        /// </summary>
        public ColumnAttribute()
        {
            ForceToUtc = false;
        }

        /// <summary>
        ///     Constructs a new instance of the <seealso cref="ColumnAttribute" />.--构造一个新的<seealso cref =“ColumnAttribute”/>实例。
        /// </summary>
        /// <param name="name">The name of the column.--列的名称。</param>
        public ColumnAttribute(string name)
        {
            Name = name;
            ForceToUtc = false;
        }
    }


    /// <summary>
    ///     Represents the attribute which decorates a poco class to state all columns must be explicitly mapped using either a
    ///     <seealso cref="ColumnAttribute" /> or <seealso cref="ResultColumnAttribute" />.
    ///     表示装饰一个poco类的属性来声明所有列必须使用<seealso cref =“ColumnAttribute”/>或<seealso cref =“ResultColumnAttribute”/>显式映射。
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class ExplicitColumnsAttribute : Attribute
    {
    }


    /// <summary>
    ///     Represents an attribute which can decorate a Poco property to ensure PetaPoco does not map column, and therefore
    ///     ignores the column.--表示可以装饰Poco属性以确保PetaPoco不映射列的属性，因此忽略该列。
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class IgnoreAttribute : Attribute
    {
    }


    /// <summary>
    ///     Is an attribute, which when applied to a Poco class, specifies primary key column. Additionally, specifies whether
    ///     the column is auto incrementing and the optional sequence name for Oracle sequence columns.
    ///     是一个属性，当应用于Poco类时，它指定主键列。 另外，指定列是自动递增还是Oracle序列列的可选序列名称。
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class PrimaryKeyAttribute : Attribute
    {
        /// <summary>
        ///     The column name.--列名称。
        /// </summary>
        /// <returns>
        ///     The column name.--列名称。
        /// </returns>
        public string Value { get; private set; }

        /// <summary>
        ///     The sequence name.--序列名称。
        /// </summary>
        /// <returns>
        ///     The sequence name.--序列名称。
        /// </returns>
        public string SequenceName { get; set; }

        /// <summary>
        ///     A flag which specifies if the primary key is auto incrementing.--指定主键是否自动递增的标志。
        /// </summary>
        /// <returns>
        ///     True if the primary key is auto incrementing; else, False.--如果主键是自动递增，则为真; 否则，假。
        /// </returns>
        public bool AutoIncrement { get; set; }

        /// <summary>
        ///     Constructs a new instance of the <seealso cref="PrimaryKeyAttribute" />.--构造一个新的<seealso cref =“PrimaryKeyAttribute”/>实例。
        /// </summary>
        /// <param name="primaryKey">The name of the primary key column.--主键列的名称</param>
        public PrimaryKeyAttribute(string primaryKey)
        {
            Value = primaryKey;
            AutoIncrement = true;
        }
    }


    /// <summary>
    ///     Represents an attribute which can decorate a poco property as a result only column. A result only column is a
    ///     column that is only populated in queries and is not used for updates or inserts operations.
    ///     表示一个属性，可以仅结果列装饰poco属性。 仅结果列是仅在查询中填充的列，不用于更新或插入操作。
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    public class ResultColumnAttribute : ColumnAttribute
    {
        /// <summary>
        ///     Constructs a new instance of the <seealso cref="ResultColumnAttribute" />.--构造一个新的<seealso cref =“ResultColumnAttribute”/>实例。
        /// </summary>
        public ResultColumnAttribute()
        {
        }

        /// <summary>
        ///     Constructs a new instance of the <seealso cref="ResultColumnAttribute" />.--构造一个新的<seealso cref =“ResultColumnAttribute”/>实例。
        /// </summary>
        /// <param name="name">The name of the DB column.</param>--DB列的名称。
        public ResultColumnAttribute(string name)
            : base(name)
        {
        }
    }


    /// <summary>
    ///     Represents an attribute, which when applied to a Poco class, specifies the the DB table name which it maps to--表示一个属性，当应用于Poco类时，它指定其映射到的数据库表名称
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class TableNameAttribute : Attribute
    {
        /// <summary>
        ///     The table nane of the database that this entity maps to.--该实体映射到的数据库的表名。
        /// </summary>
        /// <returns>
        ///     The table nane of the database that this entity maps to.--该实体映射到的数据库的表名。
        /// </returns>
        public string Value { get; private set; }

        /// <summary>
        ///     Constructs a new instance of the <seealso cref="TableNameAttribute" />.--构造一个新的<seealso cref =“TableNameAttribute”/>实例。
        /// </summary>
        /// <param name="tableName">The table nane of the database that this entity maps to.--该实体映射到的数据库的表名。</param>
        public TableNameAttribute(string tableName)
        {
            Value = tableName;
        }
    }


    /// <summary>
    ///     Wrap strings in an instance of this class to force use of DBType.AnsiString--在此类的实例中包装字符串以强制使用DBType.AnsiString
    /// </summary>
    public class AnsiString
    {
        /// <summary>
        ///     The string value--字符串值
        /// </summary>
        public string Value { get; private set; }

        /// <summary>
        ///     Constructs an AnsiString--构造一个AnsiString
        /// </summary>
        /// <param name="str">The C# string to be converted to ANSI before being passed to the DB--在传递给DB之前要转换为ANSI的C＃字符串</param>
        public AnsiString(string str)
        {
            Value = str;
        }
    }


    /// <summary>
    ///     Hold information about a column in the database.--保留数据库中有关列的信息。
    /// </summary>
    /// <remarks>
    ///     Typically ColumnInfo is automatically populated from the attributes on a POCO object and it's properties. It can
    ///     however also be returned from the IMapper interface to provide your owning bindings between the DB and your POCOs.
    ///     通常，ColumnInfo将自动从POCO对象的属性中填充，并且是属性。 然而，它也可以从IMapper接口返回，以在DB和POCO之间提供自己的绑定。
    /// </remarks>
    public class ColumnInfo
    {
        /// <summary>
        ///     The SQL name of the column--列的SQL名称
        /// </summary>
        public string ColumnName { get; set; }

        /// <summary>
        ///     True if this column returns a calculated value from the database and shouldn't be used in Insert and Update
        ///     operations.
        /// </summary>
        public bool ResultColumn { get; set; }

        /// <summary>
        ///     True if time and date values returned through this column should be forced to UTC DateTimeKind. (no conversion is
        ///     applied - the Kind of the DateTime property
        ///     is simply set to DateTimeKind.Utc instead of DateTimeKind.Unknown.
        /// </summary>
        public bool ForceToUtc { get; set; }

        /// <summary>
        ///     The insert template. If not null, this template is used for generating the insert section instead of the deafult
        ///     string.Format("{0}{1}", paramPrefix, index"). Setting this allows DB related interactions, such as "CAST({0}{1} AS
        ///     json)"
        /// </summary>
        public string InsertTemplate { get; set; }

        /// <summary>
        ///     The update template. If not null, this template is used for generating the update section instead of the deafult
        ///     string.Format("{0} = {1}{2}", colName, paramPrefix, index"). Setting this allows DB related interactions, such as "{0} = CAST({1}{2} AS
        ///     json)"
        /// </summary>
        public string UpdateTemplate { get; set; }

        /// <summary>
        ///     Creates and populates a ColumnInfo from the attributes of a POCO property.
        /// </summary>
        /// <param name="propertyInfo">The property whose column info is required</param>
        /// <returns>A ColumnInfo instance</returns>
        public static ColumnInfo FromProperty(PropertyInfo propertyInfo)
        {
            // Check if declaring poco has [Explicit] attribute
            var explicitColumns =
                propertyInfo.DeclaringType.GetCustomAttributes(typeof(ExplicitColumnsAttribute), true).Length > 0;

            // Check for [Column]/[Ignore] Attributes
            var colAttrs = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), true);
            if (explicitColumns)
            {
                if (colAttrs.Length == 0)
                    return null;
            }
            else
            {
                if (propertyInfo.GetCustomAttributes(typeof(IgnoreAttribute), true).Length != 0)
                    return null;
            }

            var ci = new ColumnInfo();

            // Read attribute
            if (colAttrs.Length > 0)
            {
                var colattr = (ColumnAttribute)colAttrs[0];
                ci.InsertTemplate = colattr.InsertTemplate;
                ci.UpdateTemplate = colattr.UpdateTemplate;
                ci.ColumnName = colattr.Name == null ? propertyInfo.Name : colattr.Name;
                ci.ForceToUtc = colattr.ForceToUtc;
                if ((colattr as ResultColumnAttribute) != null)
                    ci.ResultColumn = true;
            }
            else
            {
                ci.ColumnName = propertyInfo.Name;
                ci.ForceToUtc = false;
                ci.ResultColumn = false;
            }

            return ci;
        }
    }


    /// <summary>
    ///     Represents a configurable convention mapper.
    /// </summary>
    /// <remarks>
    ///     By default this mapper replaces <see cref="StandardMapper" /> without change, which means backwards compatibility
    ///     is kept.
    /// </remarks>
    public class ConventionMapper : IMapper
    {
        /// <summary>
        ///     Gets or sets the get sequence name logic.
        /// </summary>
        public Func<Type, PropertyInfo, string> GetSequenceName { get; set; }

        /// <summary>
        ///     Gets or sets the inflect column name logic.
        /// </summary>
        public Func<IInflector, string, string> InflectColumnName { get; set; }

        /// <summary>
        ///     Gets or sets the inflect table name logic.
        /// </summary>
        public Func<IInflector, string, string> InflectTableName { get; set; }

        /// <summary>
        ///     Gets or sets the is primary key auto increment logic.
        /// </summary>
        public Func<Type, bool> IsPrimaryKeyAutoIncrement { get; set; }

        /// <summary>
        ///     Gets or sets the map column logic.
        /// </summary>
        public Func<ColumnInfo, Type, PropertyInfo, bool> MapColumn { get; set; }

        /// <summary>
        ///     Gets or set the map primary key logic.
        /// </summary>
        public Func<TableInfo, Type, bool> MapPrimaryKey { get; set; }

        /// <summary>
        ///     Gets or sets the map table logic.
        /// </summary>
        public Func<TableInfo, Type, bool> MapTable { get; set; }

        /// <summary>
        ///     Gets or sets the from db convert logic.
        /// </summary>
        public Func<PropertyInfo, Type, Func<object, object>> FromDbConverter { get; set; }

        /// <summary>
        ///     Gets or sets the to db converter logic.
        /// </summary>
        public Func<PropertyInfo, Func<object, object>> ToDbConverter { get; set; }

        /// <summary>
        ///     Constructs a new instance of convention mapper.
        /// </summary>
        public ConventionMapper()
        {
            GetSequenceName = (t, pi) => null;
            InflectColumnName = (inflect, cn) => cn;
            InflectTableName = (inflect, tn) => tn;
            MapPrimaryKey = (ti, t) =>
            {
                var primaryKey = t.GetCustomAttributes(typeof(PrimaryKeyAttribute), true).FirstOrDefault() as PrimaryKeyAttribute;

                if (primaryKey != null)
                {
                    ti.PrimaryKey = primaryKey.Value;
                    ti.SequenceName = primaryKey.SequenceName;
                    ti.AutoIncrement = primaryKey.AutoIncrement;
                    return true;
                }

                var prop = t.GetProperties().FirstOrDefault(p =>
                {
                    if (p.Name.Equals("Id", StringComparison.OrdinalIgnoreCase))
                        return true;
                    if (p.Name.Equals(t.Name + "Id", StringComparison.OrdinalIgnoreCase))
                        return true;
                    if (p.Name.Equals(t.Name + "_Id", StringComparison.OrdinalIgnoreCase))
                        return true;
                    return false;
                });

                if (prop == null)
                    return false;

                ti.PrimaryKey = InflectColumnName(Inflector.Instance, prop.Name);
                ti.AutoIncrement = IsPrimaryKeyAutoIncrement(prop.PropertyType);
                ti.SequenceName = GetSequenceName(t, prop);
                return true;
            };
            MapTable = (ti, t) =>
            {
                var tableName = t.GetCustomAttributes(typeof(TableNameAttribute), true).FirstOrDefault() as TableNameAttribute;
                ti.TableName = tableName != null ? tableName.Value : InflectTableName(Inflector.Instance, t.Name);
                MapPrimaryKey(ti, t);
                return true;
            };
            IsPrimaryKeyAutoIncrement = t =>
            {
                if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
                    t = t.GetGenericArguments()[0];

                if (t == typeof(long) || t == typeof(ulong))
                    return true;
                if (t == typeof(int) || t == typeof(uint))
                    return true;
                if (t == typeof(short) || t == typeof(ushort))
                    return true;

                return false;
            };
            MapColumn = (ci, t, pi) =>
            {
                // Check if declaring poco has [Explicit] attribute
                var isExplicit = t.GetCustomAttributes(typeof(ExplicitColumnsAttribute), true).Any();

                // Check for [Column]/[Ignore] Attributes
                var column = pi.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault() as ColumnAttribute;

                if (isExplicit && column == null)
                    return false;

                if (pi.GetCustomAttributes(typeof(IgnoreAttribute), true).Any())
                    return false;

                // Read attribute
                if (column != null)
                {
                    ci.ColumnName = column.Name ?? InflectColumnName(Inflector.Instance, pi.Name);
                    ci.ForceToUtc = column.ForceToUtc;
                    ci.ResultColumn = (column as ResultColumnAttribute) != null;
                    ci.InsertTemplate = column.InsertTemplate;
                    ci.UpdateTemplate = column.UpdateTemplate;
                }
                else
                {
                    ci.ColumnName = InflectColumnName(Inflector.Instance, pi.Name);
                }

                return true;
            };
        }

        /// <summary>
        ///     Get information about the table associated with a POCO class
        /// </summary>
        /// <param name="pocoType">The poco type.</param>
        /// <returns>A TableInfo instance</returns>
        /// <remarks>
        ///     This method must return a valid TableInfo.
        ///     To create a TableInfo from a POCO's attributes, use TableInfo.FromPoco
        /// </remarks>
        public TableInfo GetTableInfo(Type pocoType)
        {
            var ti = new TableInfo();
            return MapTable(ti, pocoType) ? ti : null;
        }

        /// <summary>
        ///     Get information about the column associated with a property of a POCO
        /// </summary>
        /// <param name="pocoProperty">The PropertyInfo of the property being queried</param>
        /// <returns>A reference to a ColumnInfo instance, or null to ignore this property</returns>
        /// <remarks>
        ///     To create a ColumnInfo from a property's attributes, use PropertyInfo.FromProperty
        /// </remarks>
        public ColumnInfo GetColumnInfo(PropertyInfo pocoProperty)
        {
            var ci = new ColumnInfo();
            return MapColumn(ci, pocoProperty.DeclaringType, pocoProperty) ? ci : null;
        }

        /// <summary>
        ///     Supply a function to convert a database value to the correct property value
        /// </summary>
        /// <param name="targetProperty">The target property</param>
        /// <param name="sourceType">The type of data returned by the DB</param>
        /// <returns>A Func that can do the conversion, or null for no conversion</returns>
        public Func<object, object> GetFromDbConverter(PropertyInfo targetProperty, Type sourceType)
        {
            return FromDbConverter != null ? FromDbConverter(targetProperty, sourceType) : null;
        }

        /// <summary>
        ///     Supply a function to convert a property value into a database value
        /// </summary>
        /// <param name="sourceProperty">The property to be converted</param>
        /// <returns>A Func that can do the conversion</returns>
        /// <remarks>
        ///     This conversion is only used for converting values from POCO's that are
        ///     being Inserted or Updated.
        ///     Conversion is not available for parameter values passed directly to queries.
        /// </remarks>
        public Func<object, object> GetToDbConverter(PropertyInfo sourceProperty)
        {
            return ToDbConverter != null ? ToDbConverter(sourceProperty) : null;
        }
    }


    /// <summary>
    ///     Base class for DatabaseType handlers - provides default/common handling for different database engines
    ///     DatabaseType处理程序的基类 - 为不同的数据库引擎提供默认/常规处理
    /// </summary>
    public abstract class DatabaseProvider : IProvider
    {
        /// <summary>
        ///     Gets the DbProviderFactory for this database provider.--获取此数据库提供程序的DbProviderFactory。
        /// </summary>
        /// <returns>The provider factory.--数据库提供程序的DbProviderFactory</returns>
        public abstract DbProviderFactory GetFactory();

        /// <summary>
        ///     Gets a flag for whether the DB has native support for GUID/UUID.--获取一个标志，以查看数据库是否支持GUID / UUID。
        /// </summary>
        public virtual bool HasNativeGuidSupport
        {
            get { return false; }
        }

        /// <summary>
        ///     Gets the <seealso cref="IPagingHelper" /> this provider supplies.--获取此提供者提供的<seealso cref =“IPagingHelper”/>
        /// </summary>
        public virtual IPagingHelper PagingUtility
        {
            get { return PagingHelper.Instance; }
        }

        /// <summary>
        ///     Escape a tablename into a suitable format for the associated database provider.--将表名转换为相关数据库提供程序的合适格式。
        /// </summary>
        /// <param name="tableName">
        ///     The name of the table (as specified by the client program, or as attributes on the associated
        ///     POCO class.--表的名称（由客户端程序指定，或作为关联的POCO类的属性。
        /// </param>
        /// <returns>The escaped table name--转换后的表名称</returns>
        public virtual string EscapeTableName(string tableName)
        {
            // Assume table names with "dot" are already escaped
            return tableName.IndexOf('.') >= 0 ? tableName : EscapeSqlIdentifier(tableName);
        }

        /// <summary>
        ///     Escape and arbitary SQL identifier into a format suitable for the associated database provider--将转义和任意SQL标识符转换为适合相关数据库提供者的格式
        /// </summary>
        /// <param name="sqlIdentifier">The SQL identifier to be escaped--要转义的SQL标识符</param>
        /// <returns>The escaped identifier--转义标识符</returns>
        public virtual string EscapeSqlIdentifier(string sqlIdentifier)
        {
            return string.Format("[{0}]", sqlIdentifier);
        }

        /// <summary>
        ///     Returns the prefix used to delimit parameters in SQL query strings.--返回用于在SQL查询字符串中分隔参数的前缀。
        /// </summary>
        /// <param name="connectionString">The connection string.--连接字符串。</param>
        /// <returns>The providers character for prefixing a query parameter.--用于前缀查询参数的提供者字符。</returns>
        public virtual string GetParameterPrefix(string connectionString)
        {
            return "@";
        }

        /// <summary>
        ///     Converts a supplied C# object value into a value suitable for passing to the database--将提供的C＃对象值转换为适合传递给数据库的值
        /// </summary>
        /// <param name="value">The value to convert--要转换的值</param>
        /// <returns>The converted value</returns>
        public virtual object MapParameterValue(object value)
        {
            if (value is bool)
                return ((bool)value) ? 1 : 0;

            return value;
        }

        /// <summary>
        ///     Called immediately before a command is executed, allowing for modification of the IDbCommand before it's passed to
        ///     the database provider--在执行命令之前立即调用，允许在将IDbCommand传递给数据库提供者之前进行修改
        /// </summary>
        /// <param name="cmd"></param>
        public virtual void PreExecute(IDbCommand cmd)
        {
        }

        /// <summary>
        ///     Builds an SQL query suitable for performing page based queries to the database--构建适合于对数据库执行基于页面的查询的SQL查询
        /// </summary>
        /// <param name="skip">The number of rows that should be skipped by the query--查询应该跳过的行数</param>
        /// <param name="take">The number of rows that should be retruend by the query--查询应该重新引发的行数</param>
        /// <param name="parts">The original SQL query after being parsed into it's component parts--原始的SQL查询被解析成它的组件</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL query--对SQL查询中任何嵌入参数的参数</param>
        /// <returns>The final SQL query that should be executed.--应该执行的最终SQL查询。</returns>
        public virtual string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args)
        {
            var sql = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", parts.Sql, args.Length, args.Length + 1);
            args = args.Concat(new object[] { take, skip }).ToArray();
            return sql;
        }

        /// <summary>
        ///     Returns an SQL Statement that can check for the existence of a row in the database.--返回一个可以检查数据库中是否存在行的SQL语句。
        /// </summary>
        /// <returns></returns>
        public virtual string GetExistsSql()
        {
            return "SELECT COUNT(*) FROM {0} WHERE {1}";
        }

        /// <summary>
        ///     Return an SQL expression that can be used to populate the primary key column of an auto-increment column.--返回一个可用于填充自动增量列主键列的SQL表达式。
        /// </summary>
        /// <param name="tableInfo">Table info describing the table--表信息描述表</param>
        /// <returns>An SQL expressions--一个SQL表达式</returns>
        /// <remarks>See the Oracle database type for an example of how this method is used.--有关如何使用此方法的示例，请参阅Oracle数据库类型。</remarks>
        public virtual string GetAutoIncrementExpression(TableInfo tableInfo)
        {
            return null;
        }

        /// <summary>
        ///     Returns an SQL expression that can be used to specify the return value of auto incremented columns.
        /// </summary>
        /// <param name="primaryKeyName">The primary key of the row being inserted.</param>
        /// <returns>An expression describing how to return the new primary key value</returns>
        /// <remarks>See the SQLServer database provider for an example of how this method is used.</remarks>
        public virtual string GetInsertOutputClause(string primaryKeyName)
        {
            return string.Empty;
        }

        /// <summary>
        ///     Performs an Insert operation--执行插入操作
        /// </summary>
        /// <param name="database">The calling Database object--调用数据库对象</param>
        /// <param name="cmd">The insert command to be executed--要执行的insert命令</param>
        /// <param name="primaryKeyName">The primary key of the table being inserted into--插入表的主键</param>
        /// <returns>The ID of the newly inserted record--新插入记录的ID</returns>
        public virtual object ExecuteInsert(Database database, IDbCommand cmd, string primaryKeyName)
        {
            cmd.CommandText += ";\nSELECT @@IDENTITY AS NewID;";
            return database.ExecuteScalarHelper(cmd);
        }

        /// <summary>
        ///     Returns the .net standard conforming DbProviderFactory.
        /// </summary>
        /// <param name="assemblyQualifiedNames">The assembly qualified name of the provider factory.</param>
        /// <returns>The db provider factory.</returns>
        /// <exception cref="ArgumentException">Thrown when <paramref name="assemblyQualifiedNames" /> does not match a type.</exception>
        protected DbProviderFactory GetFactory(params string[] assemblyQualifiedNames)
        {
            Type ft = null;
            foreach (var assemblyName in assemblyQualifiedNames)
            {
                ft = Type.GetType(assemblyName);

                if (ft != null)
                {
                    break;
                }
            }

            if (ft == null)
                throw new ArgumentException("Could not load the " + GetType().Name + " DbProviderFactory.");

            return (DbProviderFactory)ft.GetField("Instance").GetValue(null);
        }

        /// <summary>
        ///     Look at the type and provider name being used and instantiate a suitable DatabaseType instance.
        /// </summary>
        /// <param name="type">The type name.</param>
        /// <param name="allowDefault">A flag that when set allows the default <see cref="SqlServerDatabaseProvider"/> to be returned if not match is found.</param>
        /// <param name="connectionString">The connection string.--连接字符串。</param>
        /// <returns>The database provider.</returns>
        internal static IProvider Resolve(Type type, bool allowDefault, string connectionString)
        {
            var typeName = type.Name;

            // Try using type name first (more reliable)
            if (typeName.StartsWith("MySql"))
                return Singleton<MySqlDatabaseProvider>.Instance;
            if (typeName.StartsWith("MariaDb"))
                return Singleton<MariaDbDatabaseProvider>.Instance;
            if (typeName.StartsWith("SqlCe"))
                return Singleton<SqlServerCEDatabaseProviders>.Instance;
            if (typeName.StartsWith("Npgsql") || typeName.StartsWith("PgSql"))
                return Singleton<PostgreSQLDatabaseProvider>.Instance;
            if (typeName.StartsWith("Oracle"))
                return Singleton<OracleDatabaseProvider>.Instance;
            if (typeName.StartsWith("SQLite"))
                return Singleton<SQLiteDatabaseProvider>.Instance;
            if (typeName.Equals("SqlConnection") || typeName.Equals("SqlClientFactory"))
                return Singleton<SqlServerDatabaseProvider>.Instance;
            if (typeName.StartsWith("FbConnection") || typeName.EndsWith("FirebirdClientFactory"))
                return Singleton<FirebirdDbDatabaseProvider>.Instance;
            if (typeName.IndexOf("OleDb", StringComparison.InvariantCultureIgnoreCase) >= 0
                && (connectionString.IndexOf("Jet.OLEDB", StringComparison.InvariantCultureIgnoreCase) > 0 || connectionString.IndexOf("ACE.OLEDB", StringComparison.InvariantCultureIgnoreCase) > 0))
            {
                return Singleton<MsAccessDbDatabaseProvider>.Instance;
            }
            if (!allowDefault)
                throw new ArgumentException("Could not match `" + type.FullName + "` to a provider.", "type");

            // Assume SQL Server
            return Singleton<SqlServerDatabaseProvider>.Instance;
        }

        /// <summary>
        ///     Look at the type and provider name being used and instantiate a suitable DatabaseType instance.
        /// </summary>
        /// <param name="providerName">The provider name.</param>
        /// <param name="allowDefault">A flag that when set allows the default <see cref="SqlServerDatabaseProvider"/> to be returned if not match is found.</param>
        /// <param name="connectionString">The connection string.--连接字符串。</param>
        /// <returns>The database type.</returns>
        internal static IProvider Resolve(string providerName, bool allowDefault, string connectionString)
        {
            // Try again with provider name
            if (providerName.IndexOf("MySql", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<MySqlDatabaseProvider>.Instance;
            if (providerName.IndexOf("MariaDb", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<MariaDbDatabaseProvider>.Instance;
            if (providerName.IndexOf("SqlServerCe", StringComparison.InvariantCultureIgnoreCase) >= 0 ||
                providerName.IndexOf("SqlCeConnection", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<SqlServerCEDatabaseProviders>.Instance;
            if (providerName.IndexOf("Npgsql", StringComparison.InvariantCultureIgnoreCase) >= 0
                || providerName.IndexOf("pgsql", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<PostgreSQLDatabaseProvider>.Instance;
            if (providerName.IndexOf("Oracle", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<OracleDatabaseProvider>.Instance;
            if (providerName.IndexOf("SQLite", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<SQLiteDatabaseProvider>.Instance;
            if (providerName.IndexOf("Firebird", StringComparison.InvariantCultureIgnoreCase) >= 0 ||
                providerName.IndexOf("FbConnection", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<FirebirdDbDatabaseProvider>.Instance;
            if (providerName.IndexOf("OleDb", StringComparison.InvariantCultureIgnoreCase) >= 0
                && (connectionString.IndexOf("Jet.OLEDB", StringComparison.InvariantCultureIgnoreCase) > 0 || connectionString.IndexOf("ACE.OLEDB", StringComparison.InvariantCultureIgnoreCase) > 0))
            {
                return Singleton<MsAccessDbDatabaseProvider>.Instance;
            }
            if (providerName.IndexOf("SqlServer", StringComparison.InvariantCultureIgnoreCase) >= 0 ||
                providerName.IndexOf("System.Data.SqlClient", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<SqlServerDatabaseProvider>.Instance;

            if (!allowDefault)
                throw new ArgumentException("Could not match `" + providerName + "` to a provider.", "providerName");

            // Assume SQL Server
            return Singleton<SqlServerDatabaseProvider>.Instance;
        }

        /// <summary>
        ///     Unwraps a wrapped <see cref="DbProviderFactory"/>.
        /// </summary>
        /// <param name="factory">The factory to unwrap.</param>
        /// <returns>The unwrapped factory or the original factory if no wrapping occurred.</returns>
        internal static DbProviderFactory Unwrap(DbProviderFactory factory)
        {
            var sp = factory as IServiceProvider;

            if (sp == null)
                return factory;

            var unwrapped = sp.GetService(factory.GetType()) as DbProviderFactory;
            return unwrapped == null ? factory : Unwrap(unwrapped);
        }
    }


    internal class ExpandoColumn : PocoColumn
    {
        public override void SetValue(object target, object val)
        {
            (target as IDictionary<string, object>)[ColumnName] = val;
        }

        public override object GetValue(object target)
        {
            object val = null;
            (target as IDictionary<string, object>).TryGetValue(ColumnName, out val);
            return val;
        }

        public override object ChangeType(object val)
        {
            return val;
        }
    }


    public class GridReader : IGridReader
    {
        private IDataReader _reader;
        private IDbCommand _command;
        private readonly Database _db;
        private readonly IMapper _defaultMapper;

        /// <summary>
        /// The control structure for a multi-result set query
        /// </summary>
        /// <param name="database"></param>
        /// <param name="command"></param>
        /// <param name="reader"></param>
        /// <param name="defaultMapper"></param>
        internal GridReader(Database database, IDbCommand command, IDataReader reader, IMapper defaultMapper)
        {
            _db = database;
            _command = command;
            _reader = reader;
            _defaultMapper = defaultMapper;
        }

        #region public Read<T> methods

        /// <summary>
        /// Reads from a GridReader, returning the results as an IEnumerable collection
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <returns>An enumerable collection of result records--一个可枚举的结果记录集合</returns>
        public IEnumerable<T> Read<T>()
        {
            return SinglePocoFromIDataReader<T>(_gridIndex);
        }

        /// <summary>
        /// Perform a multi-poco read from a GridReader
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Read<T1, T2>()
        {
            return MultiPocoFromIDataReader<T1>(_gridIndex, new Type[] { typeof(T1), typeof(T2) }, null);
        }

        /// <summary>
        /// Perform a multi-poco read from a GridReader
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Read<T1, T2, T3>()
        {
            return MultiPocoFromIDataReader<T1>(_gridIndex, new Type[] { typeof(T1), typeof(T2), typeof(T3) }, null);
        }

        /// <summary>
        /// Perform a multi-poco read from a GridReader
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The forth POCO type</typeparam>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<T1> Read<T1, T2, T3, T4>()
        {
            return MultiPocoFromIDataReader<T1>(_gridIndex,
                new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, null);
        }

        /// <summary>
        /// Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Read<T1, T2, TRet>(Func<T1, T2, TRet> cb)
        {
            return MultiPocoFromIDataReader<TRet>(_gridIndex, new Type[] { typeof(T1), typeof(T2) }, cb);
        }

        /// <summary>
        /// Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Read<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb)
        {
            return MultiPocoFromIDataReader<TRet>(_gridIndex, new Type[] { typeof(T1), typeof(T2), typeof(T3) }, cb);
        }

        /// <summary>
        /// Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The forth POCO type</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        public IEnumerable<TRet> Read<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb)
        {
            return MultiPocoFromIDataReader<TRet>(_gridIndex,
                new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, cb);
        }

        #endregion

        #region PocoFromIDataReader

        /// <summary>
        /// Read data to a single poco
        /// </summary>
        /// <typeparam name="T">The type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <param name="index">Reader row to be read from the underlying IDataReader</param>
        /// <returns></returns>
        private IEnumerable<T> SinglePocoFromIDataReader<T>(int index)
        {
            if (_reader == null)
                throw new ObjectDisposedException(GetType().FullName, "The data reader has been disposed");
            if (_consumed)
                throw new InvalidOperationException(
                    "Query results must be consumed in the correct order, and each result can only be consumed once");
            _consumed = true;

            var pd = PocoData.ForType(typeof(T), _defaultMapper);
            try
            {
                while (index == _gridIndex)
                {
                    var factory =
                        pd.GetFactory(_command.CommandText, _command.Connection.ConnectionString, 0, _reader.FieldCount,
                            _reader, _defaultMapper) as Func<IDataReader, T>;

                    while (true)
                    {
                        T poco;
                        try
                        {
                            if (!_reader.Read())
                                yield break;
                            poco = factory(_reader);
                        }
                        catch (Exception x)
                        {
                            if (_db.OnException(x))
                                throw;
                            yield break;
                        }

                        yield return poco;
                    }
                }
            }
            finally // finally so that First etc progresses things even when multiple rows
            {
                if (index == _gridIndex)
                {
                    NextResult();
                }
            }
        }

        /// <summary>
        /// Read data to multiple pocos
        /// </summary>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="index">Reader row to be read from the underlying IDataReader</param>
        /// <param name="types">An array of Types representing the POCO types of the returned result set.--一组表示返回结果集的POCO类型的类型。</param>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        private IEnumerable<TRet> MultiPocoFromIDataReader<TRet>(int index, Type[] types, object cb)
        {
            if (_reader == null)
                throw new ObjectDisposedException(GetType().FullName, "The data reader has been disposed");
            if (_consumed)
                throw new InvalidOperationException(
                    "Query results must be consumed in the correct order, and each result can only be consumed once");
            _consumed = true;

            try
            {
                var cmd = _command;
                var r = _reader;

                var factory = MultiPocoFactory.GetFactory<TRet>(types, cmd.Connection.ConnectionString, cmd.CommandText, r, _defaultMapper);
                if (cb == null)
                    cb = MultiPocoFactory.GetAutoMapper(types.ToArray());
                bool bNeedTerminator = false;

                while (true)
                {
                    TRet poco;
                    try
                    {
                        if (!r.Read())
                            break;
                        poco = factory(r, cb);
                    }
                    catch (Exception x)
                    {
                        if (_db.OnException(x))
                            throw;
                        yield break;
                    }

                    if (poco != null)
                        yield return poco;
                    else
                        bNeedTerminator = true;
                }
                if (bNeedTerminator)
                {
                    var poco = (TRet)(cb as Delegate).DynamicInvoke(new object[types.Length]);
                    if (poco != null)
                        yield return poco;
                    else
                        yield break;
                }
            }
            finally
            {
                if (index == _gridIndex)
                {
                    NextResult();
                }
            }
        }

        #endregion

        #region DataReader Management

        private int _gridIndex;
        private bool _consumed;

        /// <summary>
        /// Advance the IDataReader to the NextResult, if available
        /// </summary>
        private void NextResult()
        {
            if (!_reader.NextResult())
                return;
            _gridIndex++;
            _consumed = false;
        }

        /// <summary>
        /// Dispose the grid, closing and disposing both the underlying reader, command and shared connection
        /// </summary>
        public void Dispose()
        {
            if (_reader != null)
            {
                if (!_reader.IsClosed && _command != null)
                    _command.Cancel();
                _reader.Dispose();
                _reader = null;
            }

            if (_command != null)
            {
                _command.Dispose();
                _command = null;
            }
            _db.CloseSharedConnection();
        }

        #endregion
    }


    public interface IGridReader : IDisposable
    {
        /// <summary>
        /// Reads from a GridReader, returning the results as an IEnumerable collection
        /// </summary>
        /// <typeparam name="T">The Type representing a row in the result set//类型表示结果集中的一行</typeparam>
        /// <returns>An enumerable collection of result records--一个可枚举的结果记录集合</returns>
        IEnumerable<T> Read<T>();

        /// <summary>
        /// Perform a multi-poco read from a GridReader
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Read<T1, T2>();

        /// <summary>
        /// Perform a multi-poco read from a GridReader
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Read<T1, T2, T3>();

        /// <summary>
        /// Perform a multi-poco read from a GridReader
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The forth POCO type</typeparam>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<T1> Read<T1, T2, T3, T4>();

        /// <summary>
        /// Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Read<T1, T2, TRet>(Func<T1, T2, TRet> cb);

        /// <summary>
        /// Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Read<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb);

        /// <summary>
        /// Perform a multi-poco query--执行多poco查询
        /// </summary>
        /// <typeparam name="T1">The first POCO type--第一个POCO类型</typeparam>
        /// <typeparam name="T2">The second POCO type--第二种POCO型</typeparam>
        /// <typeparam name="T3">The third POCO type--第三种POCO型</typeparam>
        /// <typeparam name="T4">The forth POCO type</typeparam>
        /// <typeparam name="TRet">The type of objects in the returned IEnumerable--返回的IEnumerable中的对象类型</typeparam>
        /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships--用于连接POCO实例的回调函数，或者为自动猜测关系的null</param>
        /// <returns>A collection of POCO's as an IEnumerable--POCO作为IEnumerable的集合</returns>
        IEnumerable<TRet> Read<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb);
    }


    /// <summary>
    ///     IMapper provides a way to hook into PetaPoco's Database to POCO mapping mechanism to either
    ///     customize or completely replace it.
    /// </summary>
    /// <remarks>
    ///     To use this functionality, instantiate a class that implements IMapper and then pass it to
    ///     PetaPoco through the static method Mappers.Register()
    /// </remarks>
    public interface IMapper
    {
        /// <summary>
        ///     Get information about the table associated with a POCO class
        /// </summary>
        /// <param name="pocoType">The poco type.</param>
        /// <returns>A TableInfo instance</returns>
        /// <remarks>
        ///     This method must return a valid TableInfo.
        ///     To create a TableInfo from a POCO's attributes, use TableInfo.FromPoco
        /// </remarks>
        TableInfo GetTableInfo(Type pocoType);

        /// <summary>
        ///     Get information about the column associated with a property of a POCO
        /// </summary>
        /// <param name="pocoProperty">The PropertyInfo of the property being queried</param>
        /// <returns>A reference to a ColumnInfo instance, or null to ignore this property</returns>
        /// <remarks>
        ///     To create a ColumnInfo from a property's attributes, use PropertyInfo.FromProperty
        /// </remarks>
        ColumnInfo GetColumnInfo(PropertyInfo pocoProperty);

        /// <summary>
        ///     Supply a function to convert a database value to the correct property value
        /// </summary>
        /// <param name="targetProperty">The target property</param>
        /// <param name="sourceType">The type of data returned by the DB</param>
        /// <returns>A Func that can do the conversion, or null for no conversion</returns>
        Func<object, object> GetFromDbConverter(PropertyInfo targetProperty, Type sourceType);

        /// <summary>
        ///     Supply a function to convert a property value into a database value
        /// </summary>
        /// <param name="sourceProperty">The property to be converted</param>
        /// <returns>A Func that can do the conversion</returns>
        /// <remarks>
        ///     This conversion is only used for converting values from POCO's that are
        ///     being Inserted or Updated.
        ///     Conversion is not available for parameter values passed directly to queries.
        /// </remarks>
        Func<object, object> GetToDbConverter(PropertyInfo sourceProperty);
    }


    /// <summary>
    ///     Represents a contract for a database type provider.
    /// </summary>
    public interface IProvider
    {
        /// <summary>
        ///     Gets the <seealso cref="IPagingHelper" /> this provider supplies.--获取此提供者提供的<seealso cref =“IPagingHelper”/>
        /// </summary>
        IPagingHelper PagingUtility { get; }

        /// <summary>
        ///     Gets a flag for whether the DB has native support for GUID/UUID..--获取一个标志，以查看数据库是否支持GUID / UUID。
        /// </summary>
        bool HasNativeGuidSupport { get; }

        /// <summary>
        ///     Escape a tablename into a suitable format for the associated database provider.--将表名转换为相关数据库提供程序的合适格式。
        /// </summary>
        /// <param name="tableName">
        ///     The name of the table (as specified by the client program, or as attributes on the associated
        ///     POCO class.--表的名称（由客户端程序指定，或作为关联的POCO类的属性。
        /// </param>
        /// <returns>The escaped table name--转换后的表名称</returns>
        string EscapeTableName(string tableName);

        /// <summary>
        ///     Escape and arbitary SQL identifier into a format suitable for the associated database provider--将转义和任意SQL标识符转换为适合相关数据库提供者的格式
        /// </summary>
        /// <param name="sqlIdentifier">The SQL identifier to be escaped--要转义的SQL标识符</param>
        /// <returns>The escaped identifier--转义标识符</returns>
        string EscapeSqlIdentifier(string sqlIdentifier);

        /// <summary>
        ///     Builds an SQL query suitable for performing page based queries to the database--构建适合于对数据库执行基于页面的查询的SQL查询
        /// </summary>
        /// <param name="skip">The number of rows that should be skipped by the query--查询应该跳过的行数</param>
        /// <param name="take">The number of rows that should be retruend by the query--查询应该重新引发的行数</param>
        /// <param name="parts">The original SQL query after being parsed into it's component parts--原始的SQL查询被解析成它的组件</param>
        /// <param name="args">Arguments to any embedded parameters in the SQL query--对SQL查询中任何嵌入参数的参数</param>
        /// <returns>The final SQL query that should be executed.--应该执行的最终SQL查询。</returns>
        string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args);

        /// <summary>
        ///     Converts a supplied C# object value into a value suitable for passing to the database--将提供的C＃对象值转换为适合传递给数据库的值
        /// </summary>
        /// <param name="value">The value to convert--要转换的值</param>
        /// <returns>The converted value</returns>
        object MapParameterValue(object value);

        /// <summary>
        ///     Called immediately before a command is executed, allowing for modification of the IDbCommand before it's passed to
        ///     the database provider--在执行命令之前立即调用，允许在将IDbCommand传递给数据库提供者之前进行修改
        /// </summary>
        /// <param name="cmd"></param>
        void PreExecute(IDbCommand cmd);

        /// <summary>
        ///     Returns an SQL Statement that can check for the existence of a row in the database.--返回一个可以检查数据库中是否存在行的SQL语句。
        /// </summary>
        /// <returns></returns>
        string GetExistsSql();

        /// <summary>
        ///     Performs an Insert operation--执行插入操作
        /// </summary>
        /// <param name="database">The calling Database object--调用数据库对象</param>
        /// <param name="cmd">The insert command to be executed--要执行的insert命令</param>
        /// <param name="primaryKeyName">The primary key of the table being inserted into--插入表的主键</param>
        /// <returns>The ID of the newly inserted record--新插入记录的ID</returns>
        object ExecuteInsert(Database database, IDbCommand cmd, string primaryKeyName);

        /// <summary>
        ///     Returns an SQL expression that can be used to specify the return value of auto incremented columns.
        /// </summary>
        /// <param name="primaryKeyName">The primary key of the row being inserted.</param>
        /// <returns>An expression describing how to return the new primary key value</returns>
        /// <remarks>See the SQLServer database provider for an example of how this method is used.</remarks>
        string GetInsertOutputClause(string primaryKeyName);

        /// <summary>
        ///     Returns the prefix used to delimit parameters in SQL query strings.--返回用于在SQL查询字符串中分隔参数的前缀。
        /// </summary>
        /// <param name="connectionString">The connection string.--连接字符串。</param>
        /// <returns>The providers character for prefixing a query parameter..--用于前缀查询参数的提供者字符。</returns>
        string GetParameterPrefix(string connectionString);

        /// <summary>
        ///     Return an SQL expression that can be used to populate the primary key column of an auto-increment column.--返回一个可用于填充自动增量列主键列的SQL表达式。
        /// </summary>
        /// <param name="tableInfo">Table info describing the table--表信息描述表</param>
        /// <returns>An SQL expressions--一个SQL表达式</returns>
        /// <remarks>See the Oracle database type for an example of how this method is used.--有关如何使用此方法的示例，请参阅Oracle数据库类型。</remarks>
        string GetAutoIncrementExpression(TableInfo tableInfo);

        DbProviderFactory GetFactory();
    }


    /// <summary>
    ///     Represents the contract for the transaction.--事务接口
    /// </summary>
    /// <remarks>
    ///     A PetaPoco helper to support transactions using the using syntax.--一个PetaPoco帮助程序，以支持使用语法的事务。
    /// </remarks>
    public interface ITransaction : IDisposable, IHideObjectMethods
    {
        /// <summary>
        ///     Completes the transaction. Not calling complete will cause the transaction to rollback on dispose.--完成事务。 不调用完成将导致事务在处理时回滚。
        /// </summary>
        void Complete();
    }


    /// <summary>
    ///     This static manages registation of IMapper instances with PetaPoco--此静态管理与PetaPoco注册IMapper实例
    /// </summary>
    public static class Mappers
    {
        private static Dictionary<object, IMapper> _mappers = new Dictionary<object, IMapper>();
        private static ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        /// <summary>
        ///     Registers a mapper for all types in a specific assembly
        /// </summary>
        /// <param name="assembly">The assembly whose types are to be managed by this mapper</param>
        /// <param name="mapper">The IMapper implementation</param>
        public static void Register(Assembly assembly, IMapper mapper)
        {
            RegisterInternal(assembly, mapper);
        }

        /// <summary>
        ///     Registers a mapper for a single POCO type
        /// </summary>
        /// <param name="type">The type to be managed by this mapper</param>
        /// <param name="mapper">The IMapper implementation</param>
        public static void Register(Type type, IMapper mapper)
        {
            RegisterInternal(type, mapper);
        }

        /// <summary>
        ///     Remove all mappers for all types in a specific assembly
        /// </summary>
        /// <param name="assembly">The assembly whose mappers are to be revoked</param>
        public static void Revoke(Assembly assembly)
        {
            RevokeInternal(assembly);
        }

        /// <summary>
        ///     Remove the mapper for a specific type
        /// </summary>
        /// <param name="type">The type whose mapper is to be removed</param>
        public static void Revoke(Type type)
        {
            RevokeInternal(type);
        }

        /// <summary>
        ///     Revoke an instance of a mapper
        /// </summary>
        /// <param name="mapper">The IMapper to be revkoed</param>
        public static void Revoke(IMapper mapper)
        {
            _lock.EnterWriteLock();
            try
            {
                foreach (var i in _mappers.Where(kvp => kvp.Value == mapper).ToList())
                    _mappers.Remove(i.Key);
            }
            finally
            {
                _lock.ExitWriteLock();
                FlushCaches();
            }
        }

        /// <summary>
        ///     Revokes all registered mappers.
        /// </summary>
        public static void RevokeAll()
        {
            _lock.EnterWriteLock();
            try
            {
                _mappers.Clear();
            }
            finally
            {
                _lock.ExitWriteLock();
                FlushCaches();
            }
        }

        /// <summary>
        ///     Retrieve the IMapper implementation to be used for a specified POCO type.
        /// </summary>
        /// <param name="entityType">The entity type to get the mapper for.</param>
        /// <param name="defaultMapper">The default mapper to use when non is registered for the type.</param>
        /// <returns>The mapper for the given type.</returns>
        public static IMapper GetMapper(Type entityType, IMapper defaultMapper)
        {
            _lock.EnterReadLock();
            try
            {
                IMapper val;
                if (_mappers.TryGetValue(entityType, out val))
                    return val;
                if (_mappers.TryGetValue(entityType.Assembly, out val))
                    return val;

                return defaultMapper;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }

        private static void RegisterInternal(object typeOrAssembly, IMapper mapper)
        {
            _lock.EnterWriteLock();
            try
            {
                _mappers.Add(typeOrAssembly, mapper);
            }
            finally
            {
                _lock.ExitWriteLock();
                FlushCaches();
            }
        }

        private static void RevokeInternal(object typeOrAssembly)
        {
            _lock.EnterWriteLock();
            try
            {
                _mappers.Remove(typeOrAssembly);
            }
            finally
            {
                _lock.ExitWriteLock();
                FlushCaches();
            }
        }

        private static void FlushCaches()
        {
            // Whenever a mapper is registered or revoked, we have to assume any generated code is no longer valid.
            // Since this should be a rare occurrence, the simplest approach is to simply dump everything and start over.
            MultiPocoFactory.FlushCaches();
            PocoData.FlushCaches();
        }
    }


    internal class MultiPocoFactory
    {
        // Various cached stuff
        private static readonly Cache<Tuple<Type, ArrayKey<Type>, string, string>, object> MultiPocoFactories = new Cache<Tuple<Type, ArrayKey<Type>, string, string>, object>();

        private static readonly Cache<ArrayKey<Type>, object> AutoMappers = new Cache<ArrayKey<Type>, object>();

        // Instance data used by the Multipoco factory delegate - essentially a list of the nested poco factories to call
        private List<Delegate> _delegates;

        public Delegate GetItem(int index)
        {
            return _delegates[index];
        }

        // Automagically guess the property relationships between various POCOs and create a delegate that will set them up
        public static object GetAutoMapper(Type[] types)
        {
            // Build a key
            var key = new ArrayKey<Type>(types);

            return AutoMappers.Get(key, () =>
            {
                // Create a method
                var m = new DynamicMethod("petapoco_automapper", types[0], types, true);
                var il = m.GetILGenerator();

                for (var i = 1; i < types.Length; i++)
                {
                    var handled = false;
                    for (var j = i - 1; j >= 0; j--)
                    {
                        // Find the property
                        var candidates = (from p in types[j].GetProperties() where p.PropertyType == types[i] select p).ToArray();
                        if (!candidates.Any())
                            continue;
                        if (candidates.Length > 1)
                            throw new InvalidOperationException(string.Format("Can't auto join {0} as {1} has more than one property of type {0}", types[i],
                                types[j]));

                        // Generate code
                        il.Emit(OpCodes.Ldarg_S, j);
                        il.Emit(OpCodes.Ldarg_S, i);
                        il.Emit(OpCodes.Callvirt, candidates.First().GetSetMethod(true));
                        handled = true;
                    }

                    if (!handled)
                        throw new InvalidOperationException(string.Format("Can't auto join {0}", types[i]));
                }

                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ret);

                // Cache it
                return m.CreateDelegate(Expression.GetFuncType(types.Concat(types.Take(1)).ToArray()));
            });
        }

        // Find the split point in a result set for two different pocos and return the poco factory for the first
        private static Delegate FindSplitPoint(Type typeThis, Type typeNext, string connectionString, string sql, IDataReader r, ref int pos, IMapper defaultMapper)
        {
            // Last?
            if (typeNext == null)
                return PocoData.ForType(typeThis, defaultMapper).GetFactory(sql, connectionString, pos, r.FieldCount - pos, r, defaultMapper);

            // Get PocoData for the two types
            var pdThis = PocoData.ForType(typeThis, defaultMapper);
            var pdNext = PocoData.ForType(typeNext, defaultMapper);

            // Find split point
            var firstColumn = pos;
            var usedColumns = new Dictionary<string, bool>();
            for (; pos < r.FieldCount; pos++)
            {
                // Split if field name has already been used, or if the field doesn't exist in current poco but does in the next
                var fieldName = r.GetName(pos);
                if (usedColumns.ContainsKey(fieldName) || (!pdThis.Columns.ContainsKey(fieldName) && pdNext.Columns.ContainsKey(fieldName)))
                {
                    return pdThis.GetFactory(sql, connectionString, firstColumn, pos - firstColumn, r, defaultMapper);
                }
                usedColumns.Add(fieldName, true);
            }

            throw new InvalidOperationException(string.Format("Couldn't find split point between {0} and {1}", typeThis, typeNext));
        }

        // Create a multi-poco factory
        private static Func<IDataReader, object, TRet> CreateMultiPocoFactory<TRet>(Type[] types, string connectionString, string sql, IDataReader r, IMapper defaultMapper)
        {
            var m = new DynamicMethod("petapoco_multipoco_factory", typeof(TRet), new[] { typeof(MultiPocoFactory), typeof(IDataReader), typeof(object) },
                typeof(MultiPocoFactory));
            var il = m.GetILGenerator();

            // Load the callback
            il.Emit(OpCodes.Ldarg_2);

            // Call each delegate
            var dels = new List<Delegate>();
            var pos = 0;
            for (var i = 0; i < types.Length; i++)
            {
                // Add to list of delegates to call
                var del = FindSplitPoint(types[i], i + 1 < types.Length ? types[i + 1] : null, connectionString, sql, r, ref pos, defaultMapper);
                dels.Add(del);

                // Get the delegate
                il.Emit(OpCodes.Ldarg_0); // callback,this
                il.Emit(OpCodes.Ldc_I4, i); // callback,this,Index
                il.Emit(OpCodes.Callvirt, typeof(MultiPocoFactory).GetMethod("GetItem")); // callback,Delegate
                il.Emit(OpCodes.Ldarg_1); // callback,delegate, datareader

                // Call Invoke
                var tDelInvoke = del.GetType().GetMethod("Invoke");
                il.Emit(OpCodes.Callvirt, tDelInvoke); // Poco left on stack
            }

            // By now we should have the callback and the N pocos all on the stack.  Call the callback and we're done
            il.Emit(OpCodes.Callvirt, Expression.GetFuncType(types.Concat(new[] { typeof(TRet) }).ToArray()).GetMethod("Invoke"));
            il.Emit(OpCodes.Ret);

            // Finish up
            return (Func<IDataReader, object, TRet>)m.CreateDelegate(typeof(Func<IDataReader, object, TRet>), new MultiPocoFactory() { _delegates = dels });
        }

        internal static void FlushCaches()
        {
            MultiPocoFactories.Flush();
            AutoMappers.Flush();
        }

        // Get (or create) the multi-poco factory for a query
        public static Func<IDataReader, object, TRet> GetFactory<TRet>(Type[] types, string connectionString, string sql, IDataReader r, IMapper defaultMapper)
        {
            var key = Tuple.Create(typeof(TRet), new ArrayKey<Type>(types), connectionString, sql);

            return
                (Func<IDataReader, object, TRet>)MultiPocoFactories.Get(key, () => CreateMultiPocoFactory<TRet>(types, connectionString, sql, r, defaultMapper));
        }
    }


    /// <summary>
    ///     Holds the results of a paged request.
    /// </summary>
    /// <typeparam name="T">The type of Poco in the returned result set</typeparam>
    public class Page<T>
    {
        /// <summary>
        ///     The current page number contained in this page of result set
        /// </summary>
        public long CurrentPage { get; set; }

        /// <summary>
        ///     The total number of pages in the full result set
        /// </summary>
        public long TotalPages { get; set; }

        /// <summary>
        ///     The total number of records in the full result set
        /// </summary>
        public long TotalItems { get; set; }

        /// <summary>
        ///     The number of items per page
        /// </summary>
        public long ItemsPerPage { get; set; }

        /// <summary>
        ///     The actual records on this page
        /// </summary>
        public List<T> Items { get; set; }

        /// <summary>
        ///     User property to hold anything.
        /// </summary>
        public object Context { get; set; }
    }


    public class PocoColumn
    {
        public string ColumnName;
        public bool ForceToUtc;
        public PropertyInfo PropertyInfo;
        public bool ResultColumn;
        public string InsertTemplate { get; set; }
        public string UpdateTemplate { get; set; }

        public virtual void SetValue(object target, object val)
        {
            PropertyInfo.SetValue(target, val, null);
        }

        public virtual object GetValue(object target)
        {
            return PropertyInfo.GetValue(target, null);
        }

        public virtual object ChangeType(object val)
        {
            var t = PropertyInfo.PropertyType;
            if (val.GetType().IsValueType && PropertyInfo.PropertyType.IsGenericType && PropertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                t = t.GetGenericArguments()[0];

            return Convert.ChangeType(val, t);
        }
    }


    public class PocoData
    {
        private static Cache<Type, PocoData> _pocoDatas = new Cache<Type, PocoData>();
        private static List<Func<object, object>> _converters = new List<Func<object, object>>();
        private static object _converterLock = new object();
        private static MethodInfo fnGetValue = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
        private static MethodInfo fnIsDBNull = typeof(IDataRecord).GetMethod("IsDBNull");
        private static FieldInfo fldConverters = typeof(PocoData).GetField("_converters", BindingFlags.Static | BindingFlags.GetField | BindingFlags.NonPublic);
        private static MethodInfo fnListGetItem = typeof(List<Func<object, object>>).GetProperty("Item").GetGetMethod();
        private static MethodInfo fnInvoke = typeof(Func<object, object>).GetMethod("Invoke");
        private Cache<Tuple<string, string, int, int>, Delegate> PocoFactories = new Cache<Tuple<string, string, int, int>, Delegate>();
        public Type Type;
        public string[] QueryColumns { get; private set; }

        public string[] UpdateColumns
        {
            // No need to cache as it's not used by PetaPoco internally
            get { return (from c in Columns where !c.Value.ResultColumn && c.Value.ColumnName != TableInfo.PrimaryKey select c.Key).ToArray(); }
        }

        public TableInfo TableInfo { get; private set; }
        public Dictionary<string, PocoColumn> Columns { get; private set; }

        public PocoData()
        {
        }

        public PocoData(Type type, IMapper defaultMapper)
        {
            Type = type;

            // Get the mapper for this type
            var mapper = Mappers.GetMapper(type, defaultMapper);

            // Get the table info
            TableInfo = mapper.GetTableInfo(type);

            // Work out bound properties
            Columns = new Dictionary<string, PocoColumn>(StringComparer.OrdinalIgnoreCase);
            foreach (var pi in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
            {
                ColumnInfo ci = mapper.GetColumnInfo(pi);
                if (ci == null)
                    continue;

                var pc = new PocoColumn();
                pc.PropertyInfo = pi;
                pc.ColumnName = ci.ColumnName;
                pc.ResultColumn = ci.ResultColumn;
                pc.ForceToUtc = ci.ForceToUtc;
                pc.InsertTemplate = ci.InsertTemplate;
                pc.UpdateTemplate = ci.UpdateTemplate;

                // Store it
                Columns.Add(pc.ColumnName, pc);
            }

            // Build column list for automatic select
            QueryColumns = (from c in Columns where !c.Value.ResultColumn select c.Key).ToArray();
        }

        public static PocoData ForObject(object obj, string primaryKeyName, IMapper defaultMapper)
        {
            var t = obj.GetType();
            if (t == typeof(System.Dynamic.ExpandoObject))
            {
                var pd = new PocoData();
                pd.TableInfo = new TableInfo();
                pd.Columns = new Dictionary<string, PocoColumn>(StringComparer.OrdinalIgnoreCase);
                pd.Columns.Add(primaryKeyName, new ExpandoColumn() { ColumnName = primaryKeyName });
                pd.TableInfo.PrimaryKey = primaryKeyName;
                pd.TableInfo.AutoIncrement = true;
                foreach (var col in (obj as IDictionary<string, object>).Keys)
                {
                    if (col != primaryKeyName)
                        pd.Columns.Add(col, new ExpandoColumn() { ColumnName = col });
                }
                return pd;
            }
            return ForType(t, defaultMapper);
        }

        public static PocoData ForType(Type type, IMapper defaultMapper)
        {
            if (type == typeof(System.Dynamic.ExpandoObject))
                throw new InvalidOperationException("Can't use dynamic types with this method");

            return _pocoDatas.Get(type, () => new PocoData(type, defaultMapper));
        }

        private static bool IsIntegralType(Type type)
        {
            var tc = Type.GetTypeCode(type);
            return tc >= TypeCode.SByte && tc <= TypeCode.UInt64;
        }

        // Create factory function that can convert a IDataReader record into a POCO
        public Delegate GetFactory(string sql, string connectionString, int firstColumn, int countColumns, IDataReader reader, IMapper defaultMapper)
        {
            // Check cache
            var key = Tuple.Create<string, string, int, int>(sql, connectionString, firstColumn, countColumns);

            return PocoFactories.Get(key, () =>
            {
                // Create the method
                var m = new DynamicMethod("petapoco_factory_" + PocoFactories.Count.ToString(), Type, new Type[] { typeof(IDataReader) }, true);
                var il = m.GetILGenerator();
                var mapper = Mappers.GetMapper(Type, defaultMapper);

                if (Type == typeof(object))
                {
                    // var poco=new T()
                    il.Emit(OpCodes.Newobj, typeof(System.Dynamic.ExpandoObject).GetConstructor(Type.EmptyTypes));          // obj

                    MethodInfo fnAdd = typeof(IDictionary<string, object>).GetMethod("Add");

                    // Enumerate all fields generating a set assignment for the column
                    for (int i = firstColumn; i < firstColumn + countColumns; i++)
                    {
                        var srcType = reader.GetFieldType(i);

                        il.Emit(OpCodes.Dup); // obj, obj
                        il.Emit(OpCodes.Ldstr, reader.GetName(i)); // obj, obj, fieldname

                        // Get the converter
                        Func<object, object> converter = mapper.GetFromDbConverter((PropertyInfo)null, srcType);

                        /*
                        if (ForceDateTimesToUtc && converter == null && srcType == typeof(DateTime))
                            converter = delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
                         */

                        // Setup stack for call to converter
                        AddConverterToStack(il, converter);

                        // r[i]
                        il.Emit(OpCodes.Ldarg_0); // obj, obj, fieldname, converter?,    rdr
                        il.Emit(OpCodes.Ldc_I4, i); // obj, obj, fieldname, converter?,  rdr,i
                        il.Emit(OpCodes.Callvirt, fnGetValue); // obj, obj, fieldname, converter?,  value

                        // Convert DBNull to null
                        il.Emit(OpCodes.Dup); // obj, obj, fieldname, converter?,  value, value
                        il.Emit(OpCodes.Isinst, typeof(DBNull)); // obj, obj, fieldname, converter?,  value, (value or null)
                        var lblNotNull = il.DefineLabel();
                        il.Emit(OpCodes.Brfalse_S, lblNotNull); // obj, obj, fieldname, converter?,  value
                        il.Emit(OpCodes.Pop); // obj, obj, fieldname, converter?
                        if (converter != null)
                            il.Emit(OpCodes.Pop); // obj, obj, fieldname, 
                        il.Emit(OpCodes.Ldnull); // obj, obj, fieldname, null
                        if (converter != null)
                        {
                            var lblReady = il.DefineLabel();
                            il.Emit(OpCodes.Br_S, lblReady);
                            il.MarkLabel(lblNotNull);
                            il.Emit(OpCodes.Callvirt, fnInvoke);
                            il.MarkLabel(lblReady);
                        }
                        else
                        {
                            il.MarkLabel(lblNotNull);
                        }

                        il.Emit(OpCodes.Callvirt, fnAdd);
                    }
                }
                else if (Type.IsValueType || Type == typeof(string) || Type == typeof(byte[]))
                {
                    // Do we need to install a converter?
                    var srcType = reader.GetFieldType(0);
                    var converter = GetConverter(mapper, null, srcType, Type);

                    // "if (!rdr.IsDBNull(i))"
                    il.Emit(OpCodes.Ldarg_0); // rdr
                    il.Emit(OpCodes.Ldc_I4_0); // rdr,0
                    il.Emit(OpCodes.Callvirt, fnIsDBNull); // bool
                    var lblCont = il.DefineLabel();
                    il.Emit(OpCodes.Brfalse_S, lblCont);
                    il.Emit(OpCodes.Ldnull); // null
                    var lblFin = il.DefineLabel();
                    il.Emit(OpCodes.Br_S, lblFin);

                    il.MarkLabel(lblCont);

                    // Setup stack for call to converter
                    AddConverterToStack(il, converter);

                    il.Emit(OpCodes.Ldarg_0); // rdr
                    il.Emit(OpCodes.Ldc_I4_0); // rdr,0
                    il.Emit(OpCodes.Callvirt, fnGetValue); // value

                    // Call the converter
                    if (converter != null)
                        il.Emit(OpCodes.Callvirt, fnInvoke);

                    il.MarkLabel(lblFin);
                    il.Emit(OpCodes.Unbox_Any, Type); // value converted
                }
                else
                {
                    // var poco=new T()
                    var ctor = Type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null);
                    if (ctor == null)
                        throw new InvalidOperationException("Type [" + Type.FullName + "] should have default public or non-public constructor");

                    il.Emit(OpCodes.Newobj, ctor);

                    // Enumerate all fields generating a set assignment for the column
                    for (int i = firstColumn; i < firstColumn + countColumns; i++)
                    {
                        // Get the PocoColumn for this db column, ignore if not known
                        PocoColumn pc;
                        if (!Columns.TryGetValue(reader.GetName(i), out pc))
                            continue;

                        // Get the source type for this column
                        var srcType = reader.GetFieldType(i);
                        var dstType = pc.PropertyInfo.PropertyType;

                        // "if (!rdr.IsDBNull(i))"
                        il.Emit(OpCodes.Ldarg_0); // poco,rdr
                        il.Emit(OpCodes.Ldc_I4, i); // poco,rdr,i
                        il.Emit(OpCodes.Callvirt, fnIsDBNull); // poco,bool
                        var lblNext = il.DefineLabel();
                        il.Emit(OpCodes.Brtrue_S, lblNext); // poco

                        il.Emit(OpCodes.Dup); // poco,poco

                        // Do we need to install a converter?
                        var converter = GetConverter(mapper, pc, srcType, dstType);

                        // Fast
                        bool Handled = false;
                        if (converter == null)
                        {
                            var valuegetter = typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) });
                            if (valuegetter != null
                                && valuegetter.ReturnType == srcType
                                && (valuegetter.ReturnType == dstType || valuegetter.ReturnType == Nullable.GetUnderlyingType(dstType)))
                            {
                                il.Emit(OpCodes.Ldarg_0); // *,rdr
                                il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
                                il.Emit(OpCodes.Callvirt, valuegetter); // *,value

                                // Convert to Nullable
                                if (Nullable.GetUnderlyingType(dstType) != null)
                                {
                                    il.Emit(OpCodes.Newobj, dstType.GetConstructor(new Type[] { Nullable.GetUnderlyingType(dstType) }));
                                }

                                il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
                                Handled = true;
                            }
                        }

                        // Not so fast
                        if (!Handled)
                        {
                            // Setup stack for call to converter
                            AddConverterToStack(il, converter);

                            // "value = rdr.GetValue(i)"
                            il.Emit(OpCodes.Ldarg_0); // *,rdr
                            il.Emit(OpCodes.Ldc_I4, i); // *,rdr,i
                            il.Emit(OpCodes.Callvirt, fnGetValue); // *,value

                            // Call the converter
                            if (converter != null)
                                il.Emit(OpCodes.Callvirt, fnInvoke);

                            // Assign it
                            il.Emit(OpCodes.Unbox_Any, pc.PropertyInfo.PropertyType); // poco,poco,value
                            il.Emit(OpCodes.Callvirt, pc.PropertyInfo.GetSetMethod(true)); // poco
                        }

                        il.MarkLabel(lblNext);
                    }

                    var fnOnLoaded = RecurseInheritedTypes<MethodInfo>(Type,
                        (x) => x.GetMethod("OnLoaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null));
                    if (fnOnLoaded != null)
                    {
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Callvirt, fnOnLoaded);
                    }
                }

                il.Emit(OpCodes.Ret);

                // Cache it, return it
                return m.CreateDelegate(Expression.GetFuncType(typeof(IDataReader), Type));
            }
                );
        }

        private static void AddConverterToStack(ILGenerator il, Func<object, object> converter)
        {
            if (converter != null)
            {
                // Add the converter
                int converterIndex;

                lock (_converterLock)
                {
                    converterIndex = _converters.Count;
                    _converters.Add(converter);
                }

                // Generate IL to push the converter onto the stack
                il.Emit(OpCodes.Ldsfld, fldConverters);
                il.Emit(OpCodes.Ldc_I4, converterIndex);
                il.Emit(OpCodes.Callvirt, fnListGetItem); // Converter
            }
        }

        private static Func<object, object> GetConverter(IMapper mapper, PocoColumn pc, Type srcType, Type dstType)
        {
            Func<object, object> converter = null;

            // Get converter from the mapper
            if (pc != null)
            {
                converter = mapper.GetFromDbConverter(pc.PropertyInfo, srcType);
                if (converter != null)
                    return converter;
            }

            // Standard DateTime->Utc mapper
            if (pc != null && pc.ForceToUtc && srcType == typeof(DateTime) && (dstType == typeof(DateTime) || dstType == typeof(DateTime?)))
            {
                return delegate(object src) { return new DateTime(((DateTime)src).Ticks, DateTimeKind.Utc); };
            }

            // unwrap nullable types
            Type underlyingDstType = Nullable.GetUnderlyingType(dstType);
            if (underlyingDstType != null)
            {
                dstType = underlyingDstType;
            }

            // Forced type conversion including integral types -> enum
            if (dstType.IsEnum && IsIntegralType(srcType))
            {
                var backingDstType = Enum.GetUnderlyingType(dstType);
                if (underlyingDstType != null)
                {
                    // if dstType is Nullable<Enum>, convert to enum value
                    return delegate(object src) { return Enum.ToObject(dstType, src); };
                }
                else if (srcType != backingDstType)
                {
                    return delegate(object src) { return Convert.ChangeType(src, backingDstType, null); };
                }
            }
            else if (!dstType.IsAssignableFrom(srcType))
            {
                if (dstType.IsEnum && srcType == typeof(string))
                {
                    return delegate(object src) { return EnumMapper.EnumFromString(dstType, (string)src); };
                }
                else if (dstType == typeof(Guid) && srcType == typeof(string))
                {
                    return delegate(object src) { return Guid.Parse((string)src); };
                }
                else
                {
                    return delegate(object src) { return Convert.ChangeType(src, dstType, null); };
                }
            }

            return null;
        }

        private static T RecurseInheritedTypes<T>(Type t, Func<Type, T> cb)
        {
            while (t != null)
            {
                T info = cb(t);
                if (info != null)
                    return info;
                t = t.BaseType;
            }
            return default(T);
        }

        internal static void FlushCaches()
        {
            _pocoDatas.Flush();
        }

        public string GetColumnName(string propertyName)
        {
            return Columns.Values.First(c => c.PropertyInfo.Name.Equals(propertyName)).ColumnName;
        }
    }


    /// <summary>
    ///     A simple helper class for build SQL statements
    /// </summary>
    public class Sql
    {
        private object[] _args;
        private object[] _argsFinal;
        private Sql _rhs;

        private string _sql;
        private string _sqlFinal;

        /// <summary>
        ///     Instantiate a new SQL Builder object.  Weirdly implemented as a property but makes
        ///     for more elegantly readable fluent style construction of SQL Statements
        ///     eg: db.Query(Sql.Builder.Append(....))
        /// </summary>
        public static Sql Builder
        {
            get { return new Sql(); }
        }

        /// <summary>
        ///     Returns the final SQL statement represented by this builder
        /// </summary>
        public string SQL
        {
            get
            {
                Build();
                return _sqlFinal;
            }
        }

        /// <summary>
        ///     Gets the complete, final set of arguments collected by this builder.
        /// </summary>
        public object[] Arguments
        {
            get
            {
                Build();
                return _argsFinal;
            }
        }

        /// <summary>
        ///     Default, empty constructor
        /// </summary>
        public Sql()
        {
        }

        /// <summary>
        ///     Construct an SQL statement with the supplied SQL and arguments
        /// </summary>
        /// <param name="sql">The SQL statement or fragment</param>
        /// <param name="args">Arguments to any parameters embedded in the SQL</param>
        public Sql(string sql, params object[] args)
        {
            _sql = sql;
            _args = args;
        }

        private void Build()
        {
            // already built?
            if (_sqlFinal != null)
                return;

            // Build it
            var sb = new StringBuilder();
            var args = new List<object>();
            Build(sb, args, null);
            _sqlFinal = sb.ToString();
            _argsFinal = args.ToArray();
        }

        /// <summary>
        ///     Append another SQL builder instance to the right-hand-side of this SQL builder
        /// </summary>
        /// <param name="sql">A reference to another SQL builder instance</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql Append(Sql sql)
        {
            if (_rhs != null)
                _rhs.Append(sql);
            else
                _rhs = sql;

            _sqlFinal = null;
            return this;
        }

        /// <summary>
        ///     Append an SQL fragment to the right-hand-side of this SQL builder
        /// </summary>
        /// <param name="sql">The SQL statement or fragment</param>
        /// <param name="args">Arguments to any parameters embedded in the SQL</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql Append(string sql, params object[] args)
        {
            return Append(new Sql(sql, args));
        }

        private static bool Is(Sql sql, string sqltype)
        {
            return sql != null && sql._sql != null && sql._sql.StartsWith(sqltype, StringComparison.InvariantCultureIgnoreCase);
        }

        private void Build(StringBuilder sb, List<object> args, Sql lhs)
        {
            if (!string.IsNullOrEmpty(_sql))
            {
                // Add SQL to the string
                if (sb.Length > 0)
                {
                    sb.Append("\n");
                }

                var sql = ParametersHelper.ProcessParams(_sql, _args, args);

                if (Is(lhs, "WHERE ") && Is(this, "WHERE "))
                    sql = "AND " + sql.Substring(6);
                if (Is(lhs, "ORDER BY ") && Is(this, "ORDER BY "))
                    sql = ", " + sql.Substring(9);
                // add set clause
                if (Is(lhs, "SET ") && Is(this, "SET "))
                    sql = ", " + sql.Substring(4);

                sb.Append(sql);
            }

            // Now do rhs
            if (_rhs != null)
                _rhs.Build(sb, args, this);
        }

        /// <summary>
        ///     Appends an SQL SET clause to this SQL builder
        /// </summary>
        /// <param name="sql">The SET clause like "{field} = {value}"</param>
        /// <param name="args">Arguments to any parameters embedded in the supplied SQL</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql Set(string sql, params object[] args)
        {
            return Append(new Sql("SET " + sql, args));
        }

        /// <summary>
        ///     Appends an SQL WHERE clause to this SQL builder
        /// </summary>
        /// <param name="sql">The condition of the WHERE clause</param>
        /// <param name="args">Arguments to any parameters embedded in the supplied SQL</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql Where(string sql, params object[] args)
        {
            return Append(new Sql("WHERE (" + sql + ")", args));
        }

        /// <summary>
        ///     Appends an SQL ORDER BY clause to this SQL builder
        /// </summary>
        /// <param name="columns">A collection of SQL column names to order by</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql OrderBy(params object[] columns)
        {
            return Append(new Sql("ORDER BY " + string.Join(", ", (from x in columns select x.ToString()).ToArray())));
        }

        /// <summary>
        ///     Appends an SQL SELECT clause to this SQL builder
        /// </summary>
        /// <param name="columns">A collection of SQL column names to select</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql Select(params object[] columns)
        {
            return Append(new Sql("SELECT " + string.Join(", ", (from x in columns select x.ToString()).ToArray())));
        }

        /// <summary>
        ///     Appends an SQL FROM clause to this SQL builder
        /// </summary>
        /// <param name="tables">A collection of table names to be used in the FROM clause</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql From(params object[] tables)
        {
            return Append(new Sql("FROM " + string.Join(", ", (from x in tables select x.ToString()).ToArray())));
        }

        /// <summary>
        ///     Appends an SQL GROUP BY clause to this SQL builder
        /// </summary>
        /// <param name="columns">A collection of column names to be grouped by</param>
        /// <returns>A reference to this builder, allowing for fluent style concatenation</returns>
        public Sql GroupBy(params object[] columns)
        {
            return Append(new Sql("GROUP BY " + string.Join(", ", (from x in columns select x.ToString()).ToArray())));
        }

        private SqlJoinClause Join(string joinType, string table)
        {
            return new SqlJoinClause(Append(new Sql(joinType + table)));
        }

        /// <summary>
        ///     Appends an SQL INNER JOIN clause to this SQL builder
        /// </summary>
        /// <param name="table">The name of the table to join</param>
        /// <returns>A reference an SqlJoinClause through which the join condition can be specified</returns>
        public SqlJoinClause InnerJoin(string table)
        {
            return Join("INNER JOIN ", table);
        }

        /// <summary>
        ///     Appends an SQL LEFT JOIN clause to this SQL builder
        /// </summary>
        /// <param name="table">The name of the table to join</param>
        /// <returns>A reference an SqlJoinClause through which the join condition can be specified</returns>
        public SqlJoinClause LeftJoin(string table)
        {
            return Join("LEFT JOIN ", table);
        }

        /// <summary>
        ///     Returns the SQL statement.
        /// </summary>
        /// <summary>
        ///     Returns the final SQL statement represented by this builder
        /// </summary>
        public override string ToString()
        {
            return SQL;
        }

        /// <summary>
        ///     The SqlJoinClause is a simple helper class used in the construction of SQL JOIN statements with the SQL builder
        /// </summary>
        public class SqlJoinClause
        {
            private readonly Sql _sql;

            public SqlJoinClause(Sql sql)
            {
                _sql = sql;
            }

            /// <summary>
            ///     Appends a SQL ON clause after a JOIN statement
            /// </summary>
            /// <param name="onClause">The ON clause to be appended</param>
            /// <param name="args">Arguments to any parameters embedded in the supplied SQL</param>
            /// <returns>A reference to the parent SQL builder, allowing for fluent style concatenation</returns>
            public Sql On(string onClause, params object[] args)
            {
                return _sql.Append("ON " + onClause, args);
            }
        }
    }


    /// <summary>
    ///     StandardMapper is the default implementation of IMapper used by PetaPoco
    /// </summary>
    public class StandardMapper : IMapper
    {
        /// <summary>
        ///     Get information about the table associated with a POCO class
        /// </summary>
        /// <param name="pocoType">The poco type.</param>
        /// <returns>A TableInfo instance</returns>
        /// <remarks>
        ///     This method must return a valid TableInfo.
        ///     To create a TableInfo from a POCO's attributes, use TableInfo.FromPoco
        /// </remarks>
        public virtual TableInfo GetTableInfo(Type pocoType)
        {
            return TableInfo.FromPoco(pocoType);
        }

        /// <summary>
        ///     Get information about the column associated with a property of a POCO
        /// </summary>
        /// <param name="pocoProperty">The PropertyInfo of the property being queried</param>
        /// <returns>A reference to a ColumnInfo instance, or null to ignore this property</returns>
        /// <remarks>
        ///     To create a ColumnInfo from a property's attributes, use PropertyInfo.FromProperty
        /// </remarks>
        public virtual ColumnInfo GetColumnInfo(PropertyInfo pocoProperty)
        {
            return ColumnInfo.FromProperty(pocoProperty);
        }

        /// <summary>
        ///     Supply a function to convert a database value to the correct property value
        /// </summary>
        /// <param name="targetProperty">The target property</param>
        /// <param name="sourceType">The type of data returned by the DB</param>
        /// <returns>A Func that can do the conversion, or null for no conversion</returns>
        public virtual Func<object, object> GetFromDbConverter(PropertyInfo targetProperty, Type sourceType)
        {
            return null;
        }

        /// <summary>
        ///     Supply a function to convert a property value into a database value
        /// </summary>
        /// <param name="sourceProperty">The property to be converted</param>
        /// <returns>A Func that can do the conversion</returns>
        /// <remarks>
        ///     This conversion is only used for converting values from POCO's that are
        ///     being Inserted or Updated.
        ///     Conversion is not available for parameter values passed directly to queries.
        /// </remarks>
        public virtual Func<object, object> GetToDbConverter(PropertyInfo sourceProperty)
        {
            return null;
        }
    }


    /// <summary>
    ///     Use by IMapper to override table bindings for an object
    /// </summary>
    public class TableInfo
    {
        /// <summary>
        ///     The database table name
        /// </summary>
        public string TableName { get; set; }

        /// <summary>
        ///     The name of the primary key column of the table--表的主键列的名称
        /// </summary>
        public string PrimaryKey { get; set; }

        /// <summary>
        ///     True if the primary key column is an auto-incrementing
        /// </summary>
        public bool AutoIncrement { get; set; }

        /// <summary>
        ///     The name of the sequence used for auto-incrementing Oracle primary key fields
        /// </summary>
        public string SequenceName { get; set; }

        /// <summary>
        ///     Creates and populates a TableInfo from the attributes of a POCO
        /// </summary>
        /// <param name="t">The POCO type</param>
        /// <returns>A TableInfo instance</returns>
        public static TableInfo FromPoco(Type t)
        {
            TableInfo ti = new TableInfo();

            // Get the table name
            var a = t.GetCustomAttributes(typeof(TableNameAttribute), true);
            ti.TableName = a.Length == 0 ? t.Name : (a[0] as TableNameAttribute).Value;

            // Get the primary key
            a = t.GetCustomAttributes(typeof(PrimaryKeyAttribute), true);
            ti.PrimaryKey = a.Length == 0 ? null : (a[0] as PrimaryKeyAttribute).Value;
            ti.SequenceName = a.Length == 0 ? null : (a[0] as PrimaryKeyAttribute).SequenceName;
            ti.AutoIncrement = a.Length == 0 ? false : (a[0] as PrimaryKeyAttribute).AutoIncrement;

            if (string.IsNullOrEmpty(ti.PrimaryKey))
            {
                var prop = t.GetProperties().FirstOrDefault(p =>
                {
                    if (p.Name.Equals("id", StringComparison.OrdinalIgnoreCase))
                        return true;
                    if (p.Name.Equals(t.Name + "id", StringComparison.OrdinalIgnoreCase))
                        return true;
                    if (p.Name.Equals(t.Name + "_id", StringComparison.OrdinalIgnoreCase))
                        return true;
                    return false;
                });

                if (prop != null)
                {
                    ti.PrimaryKey = prop.Name;
                    ti.AutoIncrement = prop.PropertyType.IsValueType;
                }
            }

            return ti;
        }
    }


    /// <summary>
    ///     Transaction object helps maintain transaction depth counts
    /// </summary>
    public class Transaction : ITransaction
    {
        private Database _db;

        public Transaction(Database db)
        {
            _db = db;
            _db.BeginTransaction();
        }

        public void Complete()
        {
            _db.CompleteTransaction();
            _db = null;
        }

        public void Dispose()
        {
            if (_db != null)
                _db.AbortTransaction();
        }
    }


    /// <summary>
    ///     Author: Originally written (I believe) by Andrew Peters
    ///     Source: Scott Kirkland (https://github.com/srkirkland/Inflector)
    /// </summary>
    public class EnglishInflector : IInflector
    {
        private static readonly List<Rule> Plurals = new List<Rule>();

        private static readonly List<Rule> Singulars = new List<Rule>();

        private static readonly List<string> Uncountables = new List<string>();

        static EnglishInflector()
        {
            AddPlural("$", "s");
            AddPlural("s$", "s");
            AddPlural("(ax|test)is$", "$1es");
            AddPlural("(octop|vir|alumn|fung)us$", "$1i");
            AddPlural("(alias|status)$", "$1es");
            AddPlural("(bu)s$", "$1ses");
            AddPlural("(buffal|tomat|volcan)o$", "$1oes");
            AddPlural("([ti])um$", "$1a");
            AddPlural("sis$", "ses");
            AddPlural("(?:([^f])fe|([lr])f)$", "$1$2ves");
            AddPlural("(hive)$", "$1s");
            AddPlural("([^aeiouy]|qu)y$", "$1ies");
            AddPlural("(x|ch|ss|sh)$", "$1es");
            AddPlural("(matr|vert|ind)ix|ex$", "$1ices");
            AddPlural("([m|l])ouse$", "$1ice");
            AddPlural("^(ox)$", "$1en");
            AddPlural("(quiz)$", "$1zes");

            AddSingular("s$", "");
            AddSingular("(n)ews$", "$1ews");
            AddSingular("([ti])a$", "$1um");
            AddSingular("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis");
            AddSingular("(^analy)ses$", "$1sis");
            AddSingular("([^f])ves$", "$1fe");
            AddSingular("(hive)s$", "$1");
            AddSingular("(tive)s$", "$1");
            AddSingular("([lr])ves$", "$1f");
            AddSingular("([^aeiouy]|qu)ies$", "$1y");
            AddSingular("(s)eries$", "$1eries");
            AddSingular("(m)ovies$", "$1ovie");
            AddSingular("(x|ch|ss|sh)es$", "$1");
            AddSingular("([m|l])ice$", "$1ouse");
            AddSingular("(bus)es$", "$1");
            AddSingular("(o)es$", "$1");
            AddSingular("(shoe)s$", "$1");
            AddSingular("(cris|ax|test)es$", "$1is");
            AddSingular("(octop|vir|alumn|fung)i$", "$1us");
            AddSingular("(alias|status)es$", "$1");
            AddSingular("^(ox)en", "$1");
            AddSingular("(vert|ind)ices$", "$1ex");
            AddSingular("(matr)ices$", "$1ix");
            AddSingular("(quiz)zes$", "$1");

            AddIrregular("person", "people");
            AddIrregular("man", "men");
            AddIrregular("child", "children");
            AddIrregular("sex", "sexes");
            AddIrregular("move", "moves");
            AddIrregular("goose", "geese");
            AddIrregular("alumna", "alumnae");

            AddUncountable("equipment");
            AddUncountable("information");
            AddUncountable("rice");
            AddUncountable("money");
            AddUncountable("species");
            AddUncountable("series");
            AddUncountable("fish");
            AddUncountable("sheep");
            AddUncountable("deer");
            AddUncountable("aircraft");
        }

        /// <summary>
        ///     Pluralises a word.
        /// </summary>
        /// <example>
        ///     inflect.Pluralise("search").ShouldBe("searches");
        ///     inflect.Pluralise("stack").ShouldBe("stacks");
        ///     inflect.Pluralise("fish").ShouldBe("fish");
        /// </example>
        /// <param name="word">The word to pluralise.</param>
        /// <returns>The pluralised word.</returns>
        public string Pluralise(string word)
        {
            return ApplyRules(Plurals, word);
        }

        /// <summary>
        ///     Singularises a word.
        /// </summary>
        /// <example>
        ///     inflect.Singularise("searches").ShouldBe("search");
        ///     inflect.Singularise("stacks").ShouldBe("stack");
        ///     inflect.Singularise("fish").ShouldBe("fish");
        /// </example>
        /// <param name="word">The word to signularise.</param>
        /// <returns>The signularised word.</returns>
        public string Singularise(string word)
        {
            return ApplyRules(Singulars, word);
        }

        /// <summary>
        ///     Titleises the word. (title => Title, the_brown_fox => TheBrownFox)
        /// </summary>
        /// <example>
        ///     inflect.Titleise("some title").ShouldBe("Some Title");
        ///     inflect.Titleise("some-title").ShouldBe("Some Title");
        ///     inflect.Titleise("sometitle").ShouldBe("Sometitle");
        ///     inflect.Titleise("some_title:_the_beginning").ShouldBe("Some Title: The Beginning");
        /// </example>
        /// <param name="word">The word to titleise.</param>
        /// <returns>The titleised word.</returns>
        public string Titleise(string word)
        {
            return Regex.Replace(Humanise(Underscore(word)), @"\b([a-z])",
                match => match.Captures[0].Value.ToUpper());
        }

        /// <summary>
        ///     Humanizes the word.
        /// </summary>
        /// <example>
        ///     inflect.Humanise("some_title").ShouldBe("Some title");
        ///     inflect.Humanise("some-title").ShouldBe("Some-title");
        ///     inflect.Humanise("Some_title").ShouldBe("Some title");
        ///     inflect.Humanise("someTitle").ShouldBe("Sometitle");
        ///     inflect.Humanise("someTitle_Another").ShouldBe("Sometitle another");
        /// </example>
        /// <param name="lowercaseAndUnderscoredWord">The word to humanise.</param>
        /// <returns>The humanized word.</returns>
        public string Humanise(string lowercaseAndUnderscoredWord)
        {
            return Capitalise(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " "));
        }

        /// <summary>
        ///     Pascalises the word.
        /// </summary>
        /// <example>
        ///     inflect.Pascalise("customer").ShouldBe("Customer");
        ///     inflect.Pascalise("customer_name").ShouldBe("CustomerName");
        ///     inflect.Pascalise("customer name").ShouldBe("Customer name");
        /// </example>
        /// <param name="lowercaseAndUnderscoredWord">The word to pascalise.</param>
        /// <returns>The pascalied word.</returns>
        public string Pascalise(string lowercaseAndUnderscoredWord)
        {
            return Regex.Replace(lowercaseAndUnderscoredWord, "(?:^|_)(.)",
                match => match.Groups[1].Value.ToUpper());
        }

        /// <summary>
        ///     Camelises the word.
        /// </summary>
        /// <example>
        ///     inflect.Camelise("Customer").ShouldBe("customer");
        ///     inflect.Camelise("customer_name").ShouldBe("customerName");
        ///     inflect.Camelise("customer_first_name").ShouldBe("customerFirstName");
        ///     inflect.Camelise("customer name").ShouldBe("customer name");
        /// </example>
        /// <param name="lowercaseAndUnderscoredWord">The word to camelise.</param>
        /// <returns>The camelised word.</returns>
        public string Camelise(string lowercaseAndUnderscoredWord)
        {
            return Uncapitalise(Pascalise(lowercaseAndUnderscoredWord));
        }

        /// <summary>
        ///     Underscores the word.
        /// </summary>
        /// <example>
        ///     inflect.Underscore("SomeTitle").ShouldBe("some_title");
        ///     inflect.Underscore("someTitle").ShouldBe("some_title");
        ///     inflect.Underscore("some title that will be underscored").ShouldBe("some_title_that_will_be_underscored");
        ///     inflect.Underscore("SomeTitleThatWillBeUnderscored").ShouldBe("some_title_that_will_be_underscored");
        /// </example>
        /// <param name="pascalCasedWord">The word to underscore.</param>
        /// <returns>The underscored word.</returns>
        public string Underscore(string pascalCasedWord)
        {
            return Regex.Replace(
                Regex.Replace(
                    Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-z\d])([A-Z])",
                    "$1_$2"), @"[-\s]", "_").ToLower();
        }

        /// <summary>
        ///     Capitalises the word.
        /// </summary>
        /// <example>
        ///     inflect.Capitalise("some title").ShouldBe("Some title");
        ///     inflect.Capitalise("some Title").ShouldBe("Some title");
        ///     inflect.Capitalise("SOMETITLE").ShouldBe("Sometitle");
        ///     inflect.Capitalise("someTitle").ShouldBe("Sometitle");
        ///     inflect.Capitalise("some title goes here").ShouldBe("Some title goes here");
        /// </example>
        /// <param name="word">The word to capitalise.</param>
        /// <returns>The capitalised word.</returns>
        public string Capitalise(string word)
        {
            return word.Substring(0, 1).ToUpper() + word.Substring(1).ToLower();
        }

        /// <summary>
        ///     Uncapitalises the word.
        /// </summary>
        /// <example>
        ///     inflect.Uncapitalise("Some title").ShouldBe("some title");
        ///     inflect.Uncapitalise("Some Title").ShouldBe("some Title");
        ///     inflect.Uncapitalise("SOMETITLE").ShouldBe("sOMETITLE");
        ///     inflect.Uncapitalise("someTitle").ShouldBe("someTitle");
        ///     inflect.Uncapitalise("Some title goes here").ShouldBe("some title goes here");
        /// </example>
        /// <param name="word">The word to uncapitalise.</param>
        /// <returns>The uncapitalised word.</returns>
        public string Uncapitalise(string word)
        {
            return word.Substring(0, 1).ToLower() + word.Substring(1);
        }

        /// <summary>
        ///     Ordinalises the number.
        /// </summary>
        /// <example>
        ///     inflect.Ordinalise(0).ShouldBe("0th");
        ///     inflect.Ordinalise(1).ShouldBe("1st");
        ///     inflect.Ordinalise(2).ShouldBe("2nd");
        ///     inflect.Ordinalise(3).ShouldBe("3rd");
        ///     inflect.Ordinalise(101).ShouldBe("101st");
        ///     inflect.Ordinalise(104).ShouldBe("104th");
        ///     inflect.Ordinalise(1000).ShouldBe("1000th");
        ///     inflect.Ordinalise(1001).ShouldBe("1001st");
        /// </example>
        /// <param name="number">The number to ordinalise.</param>
        /// <returns>The ordinalised number.</returns>
        public string Ordinalise(string number)
        {
            return Ordanise(int.Parse(number), number);
        }

        /// <summary>
        ///     Ordinalises the number.
        /// </summary>
        /// <example>
        ///     inflect.Ordinalise("0").ShouldBe("0th");
        ///     inflect.Ordinalise("1").ShouldBe("1st");
        ///     inflect.Ordinalise("2").ShouldBe("2nd");
        ///     inflect.Ordinalise("3").ShouldBe("3rd");
        ///     inflect.Ordinalise("100").ShouldBe("100th");
        ///     inflect.Ordinalise("101").ShouldBe("101st");
        ///     inflect.Ordinalise("1000").ShouldBe("1000th");
        ///     inflect.Ordinalise("1001").ShouldBe("1001st");
        /// </example>
        /// <param name="number">The number to ordinalise.</param>
        /// <returns>The ordinalised number.</returns>
        public string Ordinalise(int number)
        {
            return Ordanise(number, number.ToString());
        }

        /// <summary>
        ///     Dasherises the word.
        /// </summary>
        /// <example>
        ///     inflect.Dasherise("some_title").ShouldBe("some-title");
        ///     inflect.Dasherise("some-title").ShouldBe("some-title");
        ///     inflect.Dasherise("some_title_goes_here").ShouldBe("some-title-goes-here");
        ///     inflect.Dasherise("some_title and_another").ShouldBe("some-title and-another");
        /// </example>
        /// <param name="underscoredWord">The word to dasherise.</param>
        /// <returns>The dasherised word.</returns>
        public string Dasherise(string underscoredWord)
        {
            return underscoredWord.Replace('_', '-');
        }

        private static void AddIrregular(string singular, string plural)
        {
            AddPlural("(" + singular[0] + ")" + singular.Substring(1) + "$", "$1" + plural.Substring(1));
            AddSingular("(" + plural[0] + ")" + plural.Substring(1) + "$", "$1" + singular.Substring(1));
        }

        private static void AddUncountable(string word)
        {
            Uncountables.Add(word.ToLower());
        }

        private static void AddPlural(string rule, string replacement)
        {
            Plurals.Add(new Rule(rule, replacement));
        }

        private static void AddSingular(string rule, string replacement)
        {
            Singulars.Add(new Rule(rule, replacement));
        }

        private static string ApplyRules(IList<Rule> rules, string word)
        {
            var result = word;

            if (Uncountables.Contains(word.ToLower()))
                return result;

            for (var i = rules.Count - 1; i >= 0; i--)
            {
                if ((result = rules[i].Apply(word)) != null)
                {
                    break;
                }
            }

            return result;
        }

        private static string Ordanise(int number, string numberString)
        {
            var nMod100 = number % 100;

            if (nMod100 >= 11 && nMod100 <= 13)
            {
                return numberString + "th";
            }

            switch (number % 10)
            {
                case 1:
                    return numberString + "st";
                case 2:
                    return numberString + "nd";
                case 3:
                    return numberString + "rd";
                default:
                    return numberString + "th";
            }
        }

        private class Rule
        {
            private readonly Regex _regex;

            private readonly string _replacement;

            public Rule(string pattern, string replacement)
            {
                _regex = new Regex(pattern, RegexOptions.IgnoreCase);
                _replacement = replacement;
            }

            public string Apply(string word)
            {
                return !_regex.IsMatch(word) ? null : _regex.Replace(word, _replacement);
            }
        }
    }


    /// <summary>
    ///     Specifies the inflection contract.
    /// </summary>
    public interface IInflector
    {
        /// <summary>
        ///     Pluralises a word.
        /// </summary>
        /// <example>
        ///     inflect.Pluralise("search").ShouldBe("searches");
        ///     inflect.Pluralise("stack").ShouldBe("stacks");
        ///     inflect.Pluralise("fish").ShouldBe("fish");
        /// </example>
        /// <param name="word">The word to pluralise.</param>
        /// <returns>The pluralised word.</returns>
        string Pluralise(string word);

        /// <summary>
        ///     Singularises a word.
        /// </summary>
        /// <example>
        ///     inflect.Singularise("searches").ShouldBe("search");
        ///     inflect.Singularise("stacks").ShouldBe("stack");
        ///     inflect.Singularise("fish").ShouldBe("fish");
        /// </example>
        /// <param name="word">The word to signularise.</param>
        /// <returns>The signularised word.</returns>
        string Singularise(string word);

        /// <summary>
        ///     Titleises the word. (title => Title, the_brown_fox => TheBrownFox)
        /// </summary>
        /// <example>
        ///     inflect.Titleise("some title").ShouldBe("Some Title");
        ///     inflect.Titleise("some-title").ShouldBe("Some Title");
        ///     inflect.Titleise("sometitle").ShouldBe("Sometitle");
        ///     inflect.Titleise("some_title:_the_beginning").ShouldBe("Some Title: The Beginning");
        /// </example>
        /// <param name="word">The word to titleise.</param>
        /// <returns>The titleised word.</returns>
        string Titleise(string word);

        /// <summary>
        ///     Humanizes the word.
        /// </summary>
        /// <example>
        ///     inflect.Humanise("some_title").ShouldBe("Some title");
        ///     inflect.Humanise("some-title").ShouldBe("Some-title");
        ///     inflect.Humanise("Some_title").ShouldBe("Some title");
        ///     inflect.Humanise("someTitle").ShouldBe("Sometitle");
        ///     inflect.Humanise("someTitle_Another").ShouldBe("Sometitle another");
        /// </example>
        /// <param name="lowercaseAndUnderscoredWord">The word to humanise.</param>
        /// <returns>The humanized word.</returns>
        string Humanise(string lowercaseAndUnderscoredWord);

        /// <summary>
        ///     Pascalises the word.
        /// </summary>
        /// <example>
        ///     inflect.Pascalise("customer").ShouldBe("Customer");
        ///     inflect.Pascalise("customer_name").ShouldBe("CustomerName");
        ///     inflect.Pascalise("customer name").ShouldBe("Customer name");
        /// </example>
        /// <param name="lowercaseAndUnderscoredWord">The word to pascalise.</param>
        /// <returns>The pascalied word.</returns>
        string Pascalise(string lowercaseAndUnderscoredWord);

        /// <summary>
        ///     Camelises the word.
        /// </summary>
        /// <example>
        ///     inflect.Camelise("Customer").ShouldBe("customer");
        ///     inflect.Camelise("customer_name").ShouldBe("customerName");
        ///     inflect.Camelise("customer_first_name").ShouldBe("customerFirstName");
        ///     inflect.Camelise("customer name").ShouldBe("customer name");
        /// </example>
        /// <param name="lowercaseAndUnderscoredWord">The word to camelise.</param>
        /// <returns>The camelised word.</returns>
        string Camelise(string lowercaseAndUnderscoredWord);

        /// <summary>
        ///     Underscores the word.
        /// </summary>
        /// <example>
        ///     inflect.Underscore("SomeTitle").ShouldBe("some_title");
        ///     inflect.Underscore("someTitle").ShouldBe("some_title");
        ///     inflect.Underscore("some title that will be underscored").ShouldBe("some_title_that_will_be_underscored");
        ///     inflect.Underscore("SomeTitleThatWillBeUnderscored").ShouldBe("some_title_that_will_be_underscored");
        /// </example>
        /// <param name="pascalCasedWord">The word to underscore.</param>
        /// <returns>The underscored word.</returns>
        string Underscore(string pascalCasedWord);

        /// <summary>
        ///     Capitalises the word.
        /// </summary>
        /// <example>
        ///     inflect.Capitalise("some title").ShouldBe("Some title");
        ///     inflect.Capitalise("some Title").ShouldBe("Some title");
        ///     inflect.Capitalise("SOMETITLE").ShouldBe("Sometitle");
        ///     inflect.Capitalise("someTitle").ShouldBe("Sometitle");
        ///     inflect.Capitalise("some title goes here").ShouldBe("Some title goes here");
        /// </example>
        /// <param name="word">The word to capitalise.</param>
        /// <returns>The capitalised word.</returns>
        string Capitalise(string word);

        /// <summary>
        ///     Uncapitalises the word.
        /// </summary>
        /// <example>
        ///     inflect.Uncapitalise("Some title").ShouldBe("some title");
        ///     inflect.Uncapitalise("Some Title").ShouldBe("some Title");
        ///     inflect.Uncapitalise("SOMETITLE").ShouldBe("sOMETITLE");
        ///     inflect.Uncapitalise("someTitle").ShouldBe("someTitle");
        ///     inflect.Uncapitalise("Some title goes here").ShouldBe("some title goes here");
        /// </example>
        /// <param name="word">The word to uncapitalise.</param>
        /// <returns>The uncapitalised word.</returns>
        string Uncapitalise(string word);

        /// <summary>
        ///     Ordinalises the number.
        /// </summary>
        /// <example>
        ///     inflect.Ordinalise(0).ShouldBe("0th");
        ///     inflect.Ordinalise(1).ShouldBe("1st");
        ///     inflect.Ordinalise(2).ShouldBe("2nd");
        ///     inflect.Ordinalise(3).ShouldBe("3rd");
        ///     inflect.Ordinalise(101).ShouldBe("101st");
        ///     inflect.Ordinalise(104).ShouldBe("104th");
        ///     inflect.Ordinalise(1000).ShouldBe("1000th");
        ///     inflect.Ordinalise(1001).ShouldBe("1001st");
        /// </example>
        /// <param name="number">The number to ordinalise.</param>
        /// <returns>The ordinalised number.</returns>
        string Ordinalise(string number);

        /// <summary>
        ///     Ordinalises the number.
        /// </summary>
        /// <example>
        ///     inflect.Ordinalise("0").ShouldBe("0th");
        ///     inflect.Ordinalise("1").ShouldBe("1st");
        ///     inflect.Ordinalise("2").ShouldBe("2nd");
        ///     inflect.Ordinalise("3").ShouldBe("3rd");
        ///     inflect.Ordinalise("100").ShouldBe("100th");
        ///     inflect.Ordinalise("101").ShouldBe("101st");
        ///     inflect.Ordinalise("1000").ShouldBe("1000th");
        ///     inflect.Ordinalise("1001").ShouldBe("1001st");
        /// </example>
        /// <param name="number">The number to ordinalise.</param>
        /// <returns>The ordinalised number.</returns>
        string Ordinalise(int number);

        /// <summary>
        ///     Dasherises the word.
        /// </summary>
        /// <example>
        ///     inflect.Dasherise("some_title").ShouldBe("some-title");
        ///     inflect.Dasherise("some-title").ShouldBe("some-title");
        ///     inflect.Dasherise("some_title_goes_here").ShouldBe("some-title-goes-here");
        ///     inflect.Dasherise("some_title and_another").ShouldBe("some-title and-another");
        /// </example>
        /// <param name="underscoredWord">The word to dasherise.</param>
        /// <returns>The dasherised word.</returns>
        string Dasherise(string underscoredWord);
    }


    /// <summary>
    ///     Static inflection helper
    /// </summary>
    public static class Inflector
    {
        private static IInflector _inflector;

        /// <summary>
        ///     Gets or sets the <see cref="IInflector" /> instacne.
        /// </summary>
        /// <param name="value">
        ///     The inflector to set as the default instance, or null to restore the default
        ///     <see cref="EnglishInflector" />.
        /// </param>
        /// <remarks>
        ///     By default the <see cref="EnglishInflector" /> instance used.
        /// </remarks>
        /// <returns>
        ///     The currently set <see cref="IInflector" /> instance.
        /// </returns>
        public static IInflector Instance
        {
            get { return _inflector; }
            set { _inflector = value ?? new EnglishInflector(); }
        }

        static Inflector()
        {
            _inflector = new EnglishInflector();
        }
    }


    public class FirebirdDbDatabaseProvider : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            return GetFactory("FirebirdSql.Data.FirebirdClient.FirebirdClientFactory, FirebirdSql.Data.FirebirdClient");
        }

        public override string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args)
        {
            var sql = string.Format("{0}\nROWS @{1} TO @{2}", parts.Sql, args.Length, args.Length + 1);
            args = args.Concat(new object[] { skip + 1, skip + take }).ToArray();
            return sql;
        }

        public override object ExecuteInsert(Database database, IDbCommand cmd, string primaryKeyName)
        {
            cmd.CommandText = cmd.CommandText.TrimEnd();

            if (cmd.CommandText.EndsWith(";"))
                cmd.CommandText = cmd.CommandText.Substring(0, cmd.CommandText.Length - 1);

            cmd.CommandText += " RETURNING " + EscapeSqlIdentifier(primaryKeyName) + ";";
            return database.ExecuteScalarHelper(cmd);
        }

        public override string EscapeSqlIdentifier(string sqlIdentifier)
        {
            return string.Format("\"{0}\"", sqlIdentifier);
        }
    }


    public class MariaDbDatabaseProvider : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            // MariaDb currently uses the MySql data provider
            return GetFactory("MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Culture=neutral, PublicKeyToken=c5687fc88969c44d");
        }

        public override string GetParameterPrefix(string connectionString)
        {
            if (connectionString != null && connectionString.IndexOf("Allow User Variables=true") >= 0)
                return "?";
            else
                return "@";
        }

        public override string EscapeSqlIdentifier(string sqlIdentifier)
        {
            return string.Format("`{0}`", sqlIdentifier);
        }

        public override string GetExistsSql()
        {
            return "SELECT EXISTS (SELECT 1 FROM {0} WHERE {1})";
        }
    }


    public class MsAccessDbDatabaseProvider : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            return DbProviderFactories.GetFactory("System.Data.OleDb");
        }

        public override object ExecuteInsert(Database database, IDbCommand cmd, string primaryKeyName)
        {
            database.ExecuteNonQueryHelper(cmd);
            cmd.CommandText = "SELECT @@IDENTITY AS NewID;";
            return database.ExecuteScalarHelper(cmd);
        }

        public override string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args)
        {
            throw new NotSupportedException("The Access provider does not support paging.");
        }
    }


    public class MySqlDatabaseProvider : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            return GetFactory("MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Culture=neutral, PublicKeyToken=c5687fc88969c44d");
        }

        public override string GetParameterPrefix(string connectionString)
        {
            if (connectionString != null && connectionString.IndexOf("Allow User Variables=true") >= 0)
                return "?";
            else
                return "@";
        }

        public override string EscapeSqlIdentifier(string sqlIdentifier)
        {
            return string.Format("`{0}`", sqlIdentifier);
        }

        public override string GetExistsSql()
        {
            return "SELECT EXISTS (SELECT 1 FROM {0} WHERE {1})";
        }
    }


    public class OracleDatabaseProvider : DatabaseProvider
    {
        public override string GetParameterPrefix(string connectionString)
        {
            return ":";
        }

        public override void PreExecute(IDbCommand cmd)
        {
            cmd.GetType().GetProperty("BindByName").SetValue(cmd, true, null);
            cmd.GetType().GetProperty("InitialLONGFetchSize").SetValue(cmd, -1, null);
        }

        public override string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args)
        {
            if (parts.SqlSelectRemoved.StartsWith("*"))
                throw new Exception("Query must alias '*' when performing a paged query.\neg. select t.* from table t order by t.id");

            // Same deal as SQL Server
            return Singleton<SqlServerDatabaseProvider>.Instance.BuildPageQuery(skip, take, parts, ref args);
        }

        public override DbProviderFactory GetFactory()
        {
            // "Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess" is for Oracle.ManagedDataAccess.dll
            // "Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess" is for Oracle.DataAccess.dll
            return GetFactory("Oracle.ManagedDataAccess.Client.OracleClientFactory, Oracle.ManagedDataAccess, Culture=neutral, PublicKeyToken=89b483f429c47342",
                              "Oracle.DataAccess.Client.OracleClientFactory, Oracle.DataAccess");
        }

        public override string EscapeSqlIdentifier(string sqlIdentifier)
        {
            return string.Format("\"{0}\"", sqlIdentifier.ToUpperInvariant());
        }

        public override string GetAutoIncrementExpression(TableInfo ti)
        {
            if (!string.IsNullOrEmpty(ti.SequenceName))
                return string.Format("{0}.nextval", ti.SequenceName);

            return null;
        }

        public override object ExecuteInsert(Database db, IDbCommand cmd, string primaryKeyName)
        {
            if (primaryKeyName != null)
            {
                cmd.CommandText += string.Format(" returning {0} into :newid", EscapeSqlIdentifier(primaryKeyName));
                var param = cmd.CreateParameter();
                param.ParameterName = ":newid";
                param.Value = DBNull.Value;
                param.Direction = ParameterDirection.ReturnValue;
                param.DbType = DbType.Int64;
                cmd.Parameters.Add(param);
                db.ExecuteNonQueryHelper(cmd);
                return param.Value;
            }
            else
            {
                db.ExecuteNonQueryHelper(cmd);
                return -1;
            }
        }
    }


    public class PostgreSQLDatabaseProvider : DatabaseProvider
    {
        public override bool HasNativeGuidSupport
        {
            get { return true; }
        }

        public override DbProviderFactory GetFactory()
        {
            return GetFactory("Npgsql.NpgsqlFactory, Npgsql, Culture=neutral, PublicKeyToken=5d8b90d52f46fda7");
        }

        public override string GetExistsSql()
        {
            return "SELECT CASE WHEN EXISTS(SELECT 1 FROM {0} WHERE {1}) THEN 1 ELSE 0 END";
        }

        public override object MapParameterValue(object value)
        {
            // Don't map bools to ints in PostgreSQL
            if (value.GetType() == typeof(bool))
                return value;

            return base.MapParameterValue(value);
        }

        public override string EscapeSqlIdentifier(string sqlIdentifier)
        {
            return string.Format("\"{0}\"", sqlIdentifier);
        }

        public override object ExecuteInsert(Database db, System.Data.IDbCommand cmd, string primaryKeyName)
        {
            if (primaryKeyName != null)
            {
                cmd.CommandText += string.Format("returning {0} as NewID", EscapeSqlIdentifier(primaryKeyName));
                return db.ExecuteScalarHelper(cmd);
            }
            else
            {
                db.ExecuteNonQueryHelper(cmd);
                return -1;
            }
        }
    }


    public class SQLiteDatabaseProvider : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            return GetFactory("System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Culture=neutral, PublicKeyToken=db937bc2d44ff139");
        }

        public override object MapParameterValue(object value)
        {
            if (value.GetType() == typeof(uint))
                return (long)((uint)value);

            return base.MapParameterValue(value);
        }

        public override object ExecuteInsert(Database db, System.Data.IDbCommand cmd, string primaryKeyName)
        {
            if (primaryKeyName != null)
            {
                cmd.CommandText += ";\nSELECT last_insert_rowid();";
                return db.ExecuteScalarHelper(cmd);
            }
            else
            {
                db.ExecuteNonQueryHelper(cmd);
                return -1;
            }
        }

        public override string GetExistsSql()
        {
            return "SELECT EXISTS (SELECT 1 FROM {0} WHERE {1})";
        }
    }


    public class SqlServerCEDatabaseProviders : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            return GetFactory("System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Culture=neutral, PublicKeyToken=89845dcd8080cc91");
        }

        public override string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args)
        {
            if (string.IsNullOrEmpty(parts.SqlOrderBy))
                parts.Sql += " ORDER BY ABS(1)";
            var sqlPage = string.Format("{0}\nOFFSET @{1} ROWS FETCH NEXT @{2} ROWS ONLY", parts.Sql, args.Length, args.Length + 1);
            args = args.Concat(new object[] { skip, take }).ToArray();
            return sqlPage;
        }

        public override object ExecuteInsert(Database db, System.Data.IDbCommand cmd, string primaryKeyName)
        {
            db.ExecuteNonQueryHelper(cmd);
            return db.ExecuteScalar<object>("SELECT @@@IDENTITY AS NewID;");
        }
    }


    public class SqlServerDatabaseProvider : DatabaseProvider
    {
        public override DbProviderFactory GetFactory()
        {
            return GetFactory("System.Data.SqlClient.SqlClientFactory, System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
        }

        public override string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args)
        {
            var helper = (PagingHelper)PagingUtility;
            // when the query does not contain an "order by", it is very slow--当查询不包含“order by”时，这是非常慢的
            if (helper.SimpleRegexOrderBy.IsMatch(parts.SqlSelectRemoved))
            {
                var m = helper.SimpleRegexOrderBy.Match(parts.SqlSelectRemoved);
                if (m.Success)
                {
                    var g = m.Groups[0];
                    parts.SqlSelectRemoved = parts.SqlSelectRemoved.Substring(0, g.Index);
                }
            }
            if (helper.RegexDistinct.IsMatch(parts.SqlSelectRemoved))
            {
                parts.SqlSelectRemoved = "peta_inner.* FROM (SELECT " + parts.SqlSelectRemoved + ") peta_inner";
            }
            var sqlPage = string.Format("SELECT * FROM (SELECT ROW_NUMBER() OVER ({0}) peta_rn, {1}) peta_paged WHERE peta_rn > @{2} AND peta_rn <= @{3}", parts.SqlOrderBy ?? "ORDER BY (SELECT NULL)", parts.SqlSelectRemoved, args.Length, args.Length + 1);
            args = args.Concat(new object[] { skip, skip + take }).ToArray();
            return sqlPage;
        }

        public override object ExecuteInsert(Database db, System.Data.IDbCommand cmd, string primaryKeyName)
        {
            return db.ExecuteScalarHelper(cmd);
        }

        public override string GetExistsSql()
        {
            return "IF EXISTS (SELECT 1 FROM {0} WHERE {1}) SELECT 1 ELSE SELECT 0";
        }

        public override string GetInsertOutputClause(string primaryKeyName)
        {
            return String.Format(" OUTPUT INSERTED.[{0}]", primaryKeyName);
        }
    }


    internal class ArrayKey<T>
    {
        private int _hashCode;

        private T[] _keys;

        public ArrayKey(T[] keys)
        {
            // Store the keys
            _keys = keys;

            // Calculate the hashcode
            _hashCode = 17;
            foreach (var k in keys)
            {
                _hashCode = _hashCode * 23 + (k == null ? 0 : k.GetHashCode());
            }
        }

        private bool Equals(ArrayKey<T> other)
        {
            if (other == null)
                return false;

            if (other._hashCode != _hashCode)
                return false;

            if (other._keys.Length != _keys.Length)
                return false;

            for (int i = 0; i < _keys.Length; i++)
            {
                if (!object.Equals(_keys[i], other._keys[i]))
                    return false;
            }

            return true;
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as ArrayKey<T>);
        }

        public override int GetHashCode()
        {
            return _hashCode;
        }
    }


    internal static class AutoSelectHelper
    {
        private static Regex rxSelect = new Regex(@"\A\s*(SELECT|EXECUTE|CALL|WITH|SET|DECLARE)\s",
            RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);

        private static Regex rxFrom = new Regex(@"\A\s*FROM\s",
            RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline);

        public static string AddSelectClause<T>(IProvider provider, string sql, IMapper defaultMapper)
        {
            if (sql.StartsWith(";"))
                return sql.Substring(1);

            if (!rxSelect.IsMatch(sql))
            {
                var pd = PocoData.ForType(typeof(T), defaultMapper);
                var tableName = provider.EscapeTableName(pd.TableInfo.TableName);
                string cols = pd.Columns.Count != 0
                    ? string.Join(", ", (from c in pd.QueryColumns select tableName + "." + provider.EscapeSqlIdentifier(c)).ToArray())
                    : "NULL";
                if (!rxFrom.IsMatch(sql))
                    sql = string.Format("SELECT {0} FROM {1} {2}", cols, tableName, sql);
                else
                    sql = string.Format("SELECT {0} {1}", cols, sql);
            }
            return sql;
        }
    }


    internal class Cache<TKey, TValue>
    {
        private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
        private Dictionary<TKey, TValue> _map = new Dictionary<TKey, TValue>();

        public int Count
        {
            get { return _map.Count; }
        }

        public TValue Get(TKey key, Func<TValue> factory)
        {
            // Check cache
            _lock.EnterReadLock();
            TValue val;
            try
            {
                if (_map.TryGetValue(key, out val))
                    return val;
            }
            finally
            {
                _lock.ExitReadLock();
            }

            // Cache it
            _lock.EnterWriteLock();
            try
            {
                // Check again
                if (_map.TryGetValue(key, out val))
                    return val;

                // Create it
                val = factory();

                // Store it
                _map.Add(key, val);

                // Done
                return val;
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }

        public void Flush()
        {
            // Cache it
            _lock.EnterWriteLock();
            try
            {
                _map.Clear();
            }
            finally
            {
                _lock.ExitWriteLock();
            }
        }
    }


    internal static class EnumMapper
    {
        private static Cache<Type, Dictionary<string, object>> _types = new Cache<Type, Dictionary<string, object>>();

        public static object EnumFromString(Type enumType, string value)
        {
            Dictionary<string, object> map = _types.Get(enumType, () =>
            {
                var values = Enum.GetValues(enumType);

                var newmap = new Dictionary<string, object>(values.Length, StringComparer.InvariantCultureIgnoreCase);

                foreach (var v in values)
                {
                    newmap.Add(v.ToString(), v);
                }

                return newmap;
            });

            return map[value];
        }
    }


    /// <summary>
    ///     Represents the contract for a paging helper.--代表一个分页的帮助接口
    /// </summary>
    public interface IPagingHelper
    {
        /// <summary>
        ///     Splits the given <paramref name="sql" /> into <paramref name="parts" />;--将给定的<paramref name =“sql”/>分解为<paramref name =“parts”/>;
        /// </summary>
        /// <param name="sql">The SQL to split.--要拆分的SQL</param>
        /// <param name="parts">The SQL parts.--SQL部分。</param>
        /// <returns><c>True</c> if the SQL could be split; else, <c>False</c>.--SQL可以拆分返回true，否则返回false</returns>
        bool SplitSQL(string sql, out SQLParts parts);
    }


    public class PagingHelper : IPagingHelper
    {
        public Regex RegexColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b",
            RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);

        public Regex RegexDistinct = new Regex(@"\ADISTINCT\s",
            RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);

        public Regex RegexOrderBy =
            new Regex(
                @"\bORDER\s+BY\s+(?!.*?(?:\)|\s+)AS\s)(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\[\]`""\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\[\]`""\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*",
                RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);

        public Regex SimpleRegexOrderBy = new Regex(@"\bORDER\s+BY\s+", RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);

        public static IPagingHelper Instance { get; private set; }

        static PagingHelper()
        {
            Instance = new PagingHelper();
        }

        /// <summary>
        ///     Splits the given <paramref name="sql" /> into <paramref name="parts" />;--将给定的<paramref name =“sql”/>分解为<paramref name =“parts”/>;
        /// </summary>
        /// <param name="sql">The SQL to split.--要拆分的SQL</param>
        /// <param name="parts">The SQL parts.--SQL部分。</param>
        /// <returns><c>True</c> if the SQL could be split; else, <c>False</c>.--SQL可以拆分返回true，否则返回false</returns>
        public bool SplitSQL(string sql, out SQLParts parts)
        {
            parts.Sql = sql;
            parts.SqlSelectRemoved = null;
            parts.SqlCount = null;
            parts.SqlOrderBy = null;

            // Extract the columns from "SELECT <whatever> FROM"---从“SELECT <whatever> FROM”中提取列
            var m = RegexColumns.Match(sql);
            if (!m.Success)
                return false;

            // Save column list and replace with COUNT(*)---保存列列表并替换为COUNT（*）
            var g = m.Groups[1];
            parts.SqlSelectRemoved = sql.Substring(g.Index);

            if (RegexDistinct.IsMatch(parts.SqlSelectRemoved))
                parts.SqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
            else
                parts.SqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);

            // Look for the last "ORDER BY <whatever>" clause not part of a ROW_NUMBER expression
            //查找最后一个“ORDER BY <whatever>”子句不是ROW_NUMBER表达式的一部分
            m = SimpleRegexOrderBy.Match(parts.SqlCount);
            if (m.Success)
            {
                g = m.Groups[0];
                parts.SqlOrderBy = g + parts.SqlCount.Substring(g.Index + g.Length);
                parts.SqlCount = parts.SqlCount.Substring(0, g.Index);
            }

            return true;
        }
    }


    internal static class ParametersHelper
    {
        private static Regex rxParams = new Regex(@"(?<!@)@\w+", RegexOptions.Compiled);
        // Helper to handle named parameters from object properties--帮助处理来自对象属性的命名参数
        public static string ProcessParams(string sql, object[] args_src, List<object> args_dest)
        {
            return rxParams.Replace(sql, m =>
            {
                string param = m.Value.Substring(1);

                object arg_val;

                int paramIndex;
                if (int.TryParse(param, out paramIndex))
                {
                    // Numbered parameter
                    if (paramIndex < 0 || paramIndex >= args_src.Length)
                        throw new ArgumentOutOfRangeException(string.Format("Parameter '@{0}' specified but only {1} parameters supplied (in `{2}`)", paramIndex,
                            args_src.Length, sql));
                    arg_val = args_src[paramIndex];
                }
                else
                {
                    // Look for a property on one of the arguments with this name
                    bool found = false;
                    arg_val = null;
                    foreach (var o in args_src)
                    {
                        var pi = o.GetType().GetProperty(param);
                        if (pi != null)
                        {
                            arg_val = pi.GetValue(o, null);
                            found = true;
                            break;
                        }
                    }

                    if (!found)
                        throw new ArgumentException(
                            string.Format("Parameter '@{0}' specified but none of the passed arguments have a property with this name (in '{1}')", param, sql));
                }

                // Expand collections to parameter lists
                if ((arg_val as System.Collections.IEnumerable) != null &&
                    (arg_val as string) == null &&
                    (arg_val as byte[]) == null)
                {
                    var sb = new StringBuilder();
                    foreach (var i in arg_val as System.Collections.IEnumerable)
                    {
                        sb.Append((sb.Length == 0 ? "@" : ",@") + args_dest.Count.ToString());
                        args_dest.Add(i);
                    }
                    return sb.ToString();
                }
                else
                {
                    args_dest.Add(arg_val);
                    return "@" + (args_dest.Count - 1).ToString();
                }
            }
                );
        }
    }


    internal static class Singleton<T> where T : new()
    {
        public static T Instance = new T();
    }


    /// <summary>
    ///     Presents the SQL parts.--各SQL部分的呈现
    /// </summary>
    public struct SQLParts
    {
        /// <summary>
        ///     The SQL.
        /// </summary>
        public string Sql;

        /// <summary>
        ///     The SQL count.
        /// </summary>
        public string SqlCount;

        /// <summary>
        ///     The SQL Select
        /// </summary>
        public string SqlSelectRemoved;

        /// <summary>
        ///     The SQL Order By
        /// </summary>
        public string SqlOrderBy;
    }

#pragma warning restore 1066,1570,1573,1591
}